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

3819 lines
105 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1999
//
// File: userapi.cxx
//
// Contents: User-mode APIs to the NtLm security package
//
// Main user mode entry points into this dll:
// SpUserModeInitialize
// SpInstanceInit
// SpDeleteUserModeContext
// SpInitUserModeContext
// SpMakeSignature
// SpVerifySignature
// SpSealMessage
// SpUnsealMessage
// SpGetContextToken
// SpQueryContextAttributes
// SpCompleteAuthToken
// SpFormatCredentials
// SpMarshallSupplementalCreds
// SpExportSecurityContext
// SpImportSecurityContext
//
// Helper functions:
// ReferenceUserContext
// FreeUserContext
// DereferenceUserContext
// SspGenCheckSum
// SspEncryptBuffer
// NtLmMakePackedContext(this is called in the client's process)
// NtLmCreateUserModeContext
// SspGetTokenUser
// SspCreateTokenDacl
// SspMapContext (this is called in Lsa mode)
//
// History: ChandanS 26-Jul-1996 Stolen from kerberos\client2\userapi.cxx
//
//------------------------------------------------------------------------
#include <global.h> // Globals!
#include "crc32.h" // How to use crc32
extern "C"
{
#include <nlp.h>
}
// Keep this is sync with NTLM_KERNEL_CONTEXT defined in
// security\msv_sspi\kernel\krnlapi.cxx
typedef struct _NTLM_CLIENT_CONTEXT{
union {
LIST_ENTRY Next;
KSEC_LIST_ENTRY KernelNext;
};
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_CLIENT_CONTEXT, * PNTLM_CLIENT_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 NTLM_PACKED_CONTEXT_EXPORT 1
#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"
#define NTLM_USERLIST_COUNT (16) // count of lists
#define NTLM_USERLIST_LOCK_COUNT (2) // count of locks
LIST_ENTRY NtLmUserContextList[ NTLM_USERLIST_COUNT ]; // list array.
ULONG NtLmUserContextCount[ NTLM_USERLIST_COUNT ]; // count of active contexts
RTL_RESOURCE NtLmUserContextLock[ NTLM_USERLIST_LOCK_COUNT ]; // lock array
// Counter for exported handles;never de-refed
// Should probably do a GetSystemInfo and get a space of handles that cannot
// be valid in the Lsa process
ULONG_PTR ExportedContext = 0;
NTSTATUS
SspCreateTokenDacl(
HANDLE Token
);
ULONG
HandleToListIndex(
ULONG_PTR ContextHandle
);
ULONG
__inline
ListIndexToLockIndex(
ULONG ListIndex
);
//+-------------------------------------------------------------------------
//
// Function: SpUserModeInitialize
//
// Synopsis: Initialize an the MSV1_0 DLL in a client's
// address space
//
// Effects:
//
// Arguments: LsaVersion - Version of the security dll loading the package
// PackageVersion - Version of the MSV1_0 package
// UserFunctionTable - Receives a copy of Kerberos's user mode
// function table
// pcTables - Receives count of tables returned.
//
// Requires:
//
// Returns: STATUS_SUCCESS
//
// Notes: we do what was done in SspInitLocalContexts()
// from net\svcdlls\ntlmssp\client\sign.c and more.
//
//
//--------------------------------------------------------------------------
NTSTATUS
SEC_ENTRY
SpUserModeInitialize(
IN ULONG LsaVersion,
OUT PULONG PackageVersion,
OUT PSECPKG_USER_FUNCTION_TABLE * UserFunctionTable,
OUT PULONG pcTables
)
{
NTSTATUS Status = STATUS_SUCCESS;
#if DBG
SspGlobalDbflag = SSP_CRITICAL;
if( NtLmState != NtLmLsaMode )
{
InitializeCriticalSection(&SspGlobalLogFileCritSect);
}
#endif
if (LsaVersion != SECPKG_INTERFACE_VERSION)
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
*PackageVersion = SECPKG_INTERFACE_VERSION;
NtLmUserFunctionTable.InstanceInit = SpInstanceInit;
NtLmUserFunctionTable.MakeSignature = SpMakeSignature;
NtLmUserFunctionTable.VerifySignature = SpVerifySignature;
NtLmUserFunctionTable.SealMessage = SpSealMessage;
NtLmUserFunctionTable.UnsealMessage = SpUnsealMessage;
NtLmUserFunctionTable.GetContextToken = SpGetContextToken;
NtLmUserFunctionTable.QueryContextAttributes = SpQueryContextAttributes;
NtLmUserFunctionTable.CompleteAuthToken = SpCompleteAuthToken;
NtLmUserFunctionTable.InitUserModeContext = SpInitUserModeContext;
NtLmUserFunctionTable.DeleteUserModeContext = SpDeleteUserModeContext;
NtLmUserFunctionTable.FormatCredentials = SpFormatCredentials;
NtLmUserFunctionTable.MarshallSupplementalCreds = SpMarshallSupplementalCreds;
NtLmUserFunctionTable.ExportContext = SpExportSecurityContext;
NtLmUserFunctionTable.ImportContext = SpImportSecurityContext;
*UserFunctionTable = &NtLmUserFunctionTable;
*pcTables = 1;
if ( NtLmState != NtLmLsaMode)
{
//
// SafeAllocaInitialize was already called in SpLsaModeInitialize
//
SafeAllocaInitialize(SAFEALLOCA_USE_DEFAULT,
SAFEALLOCA_USE_DEFAULT,
NtLmAllocate,
NtLmFree);
}
Cleanup:
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: ReferenceUserContext
//
// Synopsis: locates a user context in the list, refrences it
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: the context, if it is found, else NULL
//
// Notes: This was SspContextReferenceContext() in
// net\svcdlls\ntlmssp\common\context.c
//
//
//--------------------------------------------------------------------------
PNTLM_CLIENT_CONTEXT
ReferenceUserContext(
IN ULONG_PTR ContextHandle,
IN BOOLEAN RemoveContext )
{
SspPrint(( SSP_API_MORE, "Entering ReferenceUserContext for 0x%x\n", ContextHandle ));
PLIST_ENTRY ListEntry;
PNTLM_CLIENT_CONTEXT pContext = NULL;
ULONG ListIndex;
ULONG LockIndex;
ListIndex = HandleToListIndex( ContextHandle );
LockIndex = ListIndexToLockIndex( ListIndex );
RtlAcquireResourceShared(&NtLmUserContextLock[LockIndex], TRUE);
//
// Look for a match for the LsaContext, not user context
//
for (ListEntry = NtLmUserContextList[ListIndex].Flink;
ListEntry != &NtLmUserContextList[ListIndex];
ListEntry = ListEntry->Flink ) {
pContext = CONTAINING_RECORD(ListEntry, NTLM_CLIENT_CONTEXT, Next );
if (pContext->LsaContext != ContextHandle)
{
continue;
}
//
// Found it!
//
if (!RemoveContext)
{
InterlockedIncrement( (PLONG)&pContext->References );
RtlReleaseResource(&NtLmUserContextLock[LockIndex]);
}
else
{
RtlConvertSharedToExclusive(&NtLmUserContextLock[LockIndex]);
RemoveEntryList (&pContext->Next);
NtLmUserContextCount[ListIndex]--;
RtlReleaseResource(&NtLmUserContextLock[LockIndex]);
SspPrint(( SSP_API_MORE, "Delinked Context 0x%lx\n", pContext ));
}
SspPrint(( SSP_API_MORE, "Leaving ReferenceUserContext for 0x%x\n", ContextHandle));
return pContext;
}
// No match found
RtlReleaseResource(&NtLmUserContextLock[LockIndex]);
SspPrint(( SSP_API_MORE, "Leaving ReferenceUserContext for 0x%x\n", ContextHandle ));
return NULL;
}
//+-------------------------------------------------------------------------
//
// Function: FreeUserContext
//
// Synopsis: frees alloced pointers in this context and
// then frees the context
//
// Arguments: lContext - the unlinked user context
//
// Returns: STATUS_SUCCESS on success
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
FreeUserContext (
PNTLM_CLIENT_CONTEXT UserContext
)
{
SspPrint(( SSP_API_MORE, "Entering FreeUserContext for context 0x%x\n", UserContext ));
NTSTATUS Status = STATUS_SUCCESS;
if (UserContext->ContextNames != NULL)
{
NtLmFree (UserContext->ContextNames);
}
if (UserContext->ClientTokenHandle != NULL)
{
NTSTATUS IgnoreStatus;
IgnoreStatus = NtClose(UserContext->ClientTokenHandle);
ASSERT (NT_SUCCESS (IgnoreStatus));
}
SspPrint(( SSP_API_MORE, "Deleting Context 0x%x\n", UserContext));
ZeroMemory( UserContext, sizeof(*UserContext) );
NtLmFree (UserContext);
SspPrint(( SSP_API_MORE, "Leaving FreeUserContext for context 0x%x, status = 0x%x\n", Status ));
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: DereferenceUserContext
//
// Synopsis: frees alloced elements in the context, frees context
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: None
//
// Notes: This was SspContextDereferenceContext() in
// net\svcdlls\ntlmssp\common\context.c
//
//
//--------------------------------------------------------------------------
NTSTATUS
DereferenceUserContext (
PNTLM_CLIENT_CONTEXT pContext
)
{
SspPrint(( SSP_API_MORE, "Entering DereferenceUserContext 0x%lx\n", pContext ));
NTSTATUS Status = STATUS_SUCCESS;
LONG References;
//
// Decrement the reference count
//
/// RtlAcquireResourceShared(&NtLmUserContextLock, TRUE);
//// ASSERT (pContext->References >= 1);
//// References = -- pContext->References;
References = InterlockedDecrement( (PLONG)&pContext->References );
ASSERT( References >= 0 );
//// RtlReleaseResource(&NtLmUserContextLock);
//
// If the count has dropped to zero, then free all alloced stuff
//
if (References == 0)
{
Status = FreeUserContext(pContext);
}
SspPrint(( SSP_API_MORE, "Leaving DereferenceUserContext\n" ));
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: SpInstanceInit
//
// Synopsis: Initialize an instance of the NtLm package in a client's
// address space
//
// Effects:
//
// Arguments: Version - Version of the security dll loading the package
// FunctionTable - Contains helper routines for use by NtLm
// UserFunctions - Receives a copy of NtLm's user mode
// function table
//
// Requires:
//
// Returns: STATUS_SUCCESS
//
// Notes: we do what was done in SspInitLocalContexts()
// from net\svcdlls\ntlmssp\client\sign.c and more.
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpInstanceInit(
IN ULONG Version,
IN PSECPKG_DLL_FUNCTIONS DllFunctionTable,
OUT PVOID * UserFunctionTable
)
{
SspPrint(( SSP_API, "Entering SpInstanceInit\n" ));
NTSTATUS Status = STATUS_SUCCESS;
ULONG Index;
// Save the Alloc/Free functions
if( NtLmState != NtLmLsaMode )
{
NtLmState = NtLmUserMode;
}
UserFunctions = DllFunctionTable;
for( Index=0 ; Index < NTLM_USERLIST_COUNT ; Index++ )
{
InitializeListHead (&NtLmUserContextList[Index]);
}
for( Index=0 ; Index < NTLM_USERLIST_LOCK_COUNT ; Index++ )
{
__try {
RtlInitializeResource (&NtLmUserContextLock[Index]);
} __except(EXCEPTION_EXECUTE_HANDLER)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
}
SspPrint(( SSP_API, "Leaving SpInstanceInit: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpDeleteUserModeContext
//
// Synopsis: Deletes a user mode context by unlinking it and then
// dereferencing it.
//
// Effects:
//
// Arguments: ContextHandle - Lsa context handle of the context to delete
//
// Requires:
//
// Returns: STATUS_SUCCESS on success, STATUS_INVALID_HANDLE if the
// context can't be located
//
// Notes:
// If this is an exported context, send a flag back to the LSA so that
// Lsa does not call the SecpDeleteSecurityContext in the lsa process
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpDeleteUserModeContext(
IN ULONG_PTR ContextHandle
)
{
SspPrint(( SSP_API, "Entering SpDeleteUserModeContext 0x%lx\n", ContextHandle ));
PNTLM_CLIENT_CONTEXT pContext = NULL;
NTSTATUS Status = STATUS_SUCCESS, SaveStatus = STATUS_SUCCESS;
//
// Find the currently existing user context and delink it
// so that another context cannot Reference it before we
// Dereference this one.
//
pContext = ReferenceUserContext(ContextHandle, TRUE);
if (pContext == NULL)
{
//
// pContext is legally NULL when we are dealing with an incomplete
// context. This can often be the case when the second call to
// InitializeSecurityContext() fails.
//
/// Status = STATUS_INVALID_HANDLE;
Status = STATUS_SUCCESS;
SspPrint(( SSP_API_MORE, "SpDeleteUserModeContext, local pContext is NULL\n" ));
goto CleanUp;
}
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)
{
Status = DereferenceUserContext(pContext);
}
if (SaveStatus == SEC_I_NO_LSA_CONTEXT)
{
Status = SaveStatus;
}
SspPrint(( SSP_API, "Leaving SpDeleteUserModeContext: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
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:
--*/
{
//
// 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;
}
RtlCopyMemory(Key,pSessionKey,KeyLen);
SspPrint(( SSP_SESSION_KEYS, "Non NTLMv2 LM_KEY session key size: %lu key=%lx%lx\n",
KeyLen,
((DWORD*)Key)[0],
((DWORD*)Key)[1]
));
rc4_key(pRc4Key, MSV1_0_LANMAN_SESSION_KEY_LENGTH, Key);
} else {
SspPrint(( SSP_SESSION_KEYS, "Non NTLMv2 (not LM_KEY) session key size: %lu\n", 16));
rc4_key(pRc4Key, MSV1_0_USER_SESSION_KEY_LENGTH, pSessionKey);
}
}
//+-------------------------------------------------------------------------
//
// Function: SpInitUserModeContext
//
// Synopsis: Creates a user-mode context from a packed LSA mode context
//
// Effects:
//
// Arguments: ContextHandle - Lsa mode context handle for the context
// PackedContext - A marshalled buffer containing the LSA
// mode context.
//
// Requires:
//
// Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpInitUserModeContext(
IN ULONG_PTR ContextHandle,
IN PSecBuffer PackedContext
)
{
ASSERT(PackedContext);
SspPrint(( SSP_API, "Entering SpInitUserModeContext 0x%lx\n", ContextHandle ));
NTSTATUS Status = STATUS_SUCCESS;
PNTLM_CLIENT_CONTEXT pContext = NULL;
UINT Length = 0;
PNTLM_PACKED_CONTEXT pTmpContext = (PNTLM_PACKED_CONTEXT) PackedContext->pvBuffer;
ULONG ListIndex;
ULONG LockIndex;
if (PackedContext->cbBuffer < sizeof(NTLM_PACKED_CONTEXT))
{
Status = STATUS_INVALID_PARAMETER;
SspPrint(( SSP_CRITICAL, "SpInitUserModeContext, ContextData size < NTLM_CLIENT_CONTEXT\n" ));
goto Cleanup;
}
pContext = (PNTLM_CLIENT_CONTEXT) NtLmAllocate( sizeof(NTLM_CLIENT_CONTEXT) );
if (!pContext)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
SspPrint(( SSP_CRITICAL, "SpInitUserModeContext, NtLmAllocate returns NULL\n" ));
goto Cleanup;
}
//
// If ClientTokenHandle is NULL, we are being called as
// as an effect of InitializeSecurityContext, else we are
// being called because of AcceptSecurityContext
//
if (pTmpContext->ClientTokenHandle != NULL )
{
pContext->ClientTokenHandle = (HANDLE) ULongToPtr(pTmpContext->ClientTokenHandle);
if (FAILED(SspCreateTokenDacl(pContext->ClientTokenHandle)))
{
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SpInitUserModeContext, SspCreateTokenDacl failed\n" ));
goto Cleanup;
}
}
// Copy contents of PackedContext->pvBuffer to pContext
pContext->LsaContext = ContextHandle;
pContext->NegotiateFlags = pTmpContext->NegotiateFlags;
SspPrint((SSP_NEGOTIATE_FLAGS, "SpInitUserModeContext NegotiateFlags: %lx\n", pContext->NegotiateFlags));
pContext->References = 1;
//
// 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;
SspPrint(( 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->ContextNames )
{
pContext->ContextNames = (PWSTR) NtLmAllocate( pTmpContext->ContextNameLength );
if ( pContext->ContextNames == NULL )
{
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto Cleanup ;
}
RtlCopyMemory(
pContext->ContextNames,
((PUCHAR) pTmpContext) + pTmpContext->ContextNames,
pTmpContext->ContextNameLength );
}
else
{
pContext->ContextNames = NULL ;
}
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;
ListIndex = HandleToListIndex( pContext->LsaContext );
LockIndex = ListIndexToLockIndex( ListIndex );
RtlAcquireResourceExclusive(&NtLmUserContextLock[LockIndex], TRUE);
InsertHeadList ( &NtLmUserContextList[ListIndex], &pContext->Next );
NtLmUserContextCount[ListIndex]++;
RtlReleaseResource(&NtLmUserContextLock[LockIndex]);
Cleanup:
if (!NT_SUCCESS(Status))
{
if (pContext != NULL)
{
FreeUserContext(pContext);
}
}
// Let FreeContextBuffer handle freeing the virtual allocs
if (PackedContext->pvBuffer != NULL)
{
FreeContextBuffer(PackedContext->pvBuffer);
PackedContext->pvBuffer = NULL;
}
SspPrint(( SSP_API, "Leaving SpInitUserModeContext: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//
// 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
--*/
{
Crc32(pSig->CheckSum,pMessage->cbBuffer,pMessage->pvBuffer,&pSig->CheckSum);
}
VOID
SspEncryptBuffer(
IN PNTLM_CLIENT_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:
--*/
{
struct RC4_KEYSTRUCT TemporaryKey;
/// struct RC4_KEYSTRUCT * EncryptionKey = &pContext->Rc4Key;
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(struct RC4_KEYSTRUCT)
);
EncryptionKey = &TemporaryKey;
}
rc4(
EncryptionKey,
BufferSize,
(PUCHAR) Buffer
);
}
typedef enum _eSignSealOp {
eSign, // MakeSignature is calling
eVerify, // VerifySignature is calling
eSeal, // SealMessage is calling
eUnseal // UnsealMessage is calling
} eSignSealOp;
SECURITY_STATUS
SspSignSealHelper(
IN PNTLM_CLIENT_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; // aligned copy of input sig data
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;
}
//
// 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))
{
// decrypt (before checksum...) if it's not READ_ONLY
if ((Op==eUnseal)
&& !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY)
)
{
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)
)
{
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].BufferType & SECBUFFER_READONLY) &&
(pMessage->pBuffers[i].cbBuffer != 0))
{
// decrypt (before checksum...)
if (Op==eUnseal)
{
SspEncryptBuffer(
pContext,
pRc4Sched,
pMessage->pBuffers[i].cbBuffer,
pMessage->pBuffers[i].pvBuffer
);
}
SspGenCheckSum(&pMessage->pBuffers[i], &Sig);
// Encrypt
if (Op==eSeal)
{
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: SpMakeSignature
//
// Synopsis: Signs a message buffer by calculatinga checksum over all
// the non-read only data buffers and encrypting the checksum
// along with a nonce.
//
// Effects:
//
// Arguments: ContextHandle - Handle of the context to use to sign the
// message.
// QualityOfProtection - Unused flags.
// MessageBuffers - Contains an array of buffers to sign and
// to store the signature.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
//
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found.
// STATUS_BUFFER_TOO_SMALL - the signature buffer is too small
// to hold the signature
//
// Returns:
//
// Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
// routine SspHandleSignMessage. It's possible that
// bugs got copied too
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpMakeSignature(
IN ULONG_PTR ContextHandle,
IN ULONG fQOP,
IN PSecBufferDesc pMessage,
IN ULONG MessageSeqNo
)
{
SspPrint(( SSP_API, "Entering SpMakeSignature\n" ));
NTSTATUS Status = S_OK;
NTSTATUS SubStatus = S_OK;
PNTLM_CLIENT_CONTEXT pContext;
NTLMSSP_MESSAGE_SIGNATURE Sig;
NTLMSSP_MESSAGE_SIGNATURE *pSig;
UNREFERENCED_PARAMETER(fQOP);
pContext = ReferenceUserContext(ContextHandle, FALSE);
if (pContext == NULL)
{
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SpMakeSignature, ReferenceUserContext returns NULL\n" ));
goto CleanUp;
}
Status = SspSignSealHelper(
pContext,
eSign,
pMessage,
MessageSeqNo,
&Sig,
&pSig
);
if( !NT_SUCCESS(Status) ) {
SspPrint(( SSP_CRITICAL, "SpMakeSignature, SspSignSealHelper returns %lx\n", Status ));
goto CleanUp;
}
RtlCopyMemory(
pSig,
&Sig,
NTLMSSP_MESSAGE_SIGNATURE_SIZE
);
CleanUp:
if (pContext != NULL)
{
SubStatus = DereferenceUserContext(pContext);
// Don't destroy real status
if (NT_SUCCESS(Status))
{
Status = SubStatus;
}
}
SspPrint(( SSP_API, "Leaving SpMakeSignature: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpVerifySignature
//
// Synopsis: Verifies a signed message buffer by calculating a checksum over all
// the non-read only data buffers and encrypting the checksum
// along with a nonce.
//
// Effects:
//
// Arguments: ContextHandle - Handle of the context to use to sign the
// message.
// MessageBuffers - Contains an array of signed buffers and
// a signature buffer.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
// QualityOfProtection - Unused flags.
//
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found or was too small.
//
// Returns:
//
// Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
// routine SspHandleVerifyMessage. It's possible that
// bugs got copied too
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpVerifySignature(
IN ULONG_PTR ContextHandle,
IN PSecBufferDesc pMessage,
IN ULONG MessageSeqNo,
OUT PULONG pfQOP
)
{
SspPrint(( SSP_API, "Entering SpVerifySignature\n" ));
NTSTATUS Status = S_OK;
NTSTATUS SubStatus = S_OK;
PNTLM_CLIENT_CONTEXT pContext;
NTLMSSP_MESSAGE_SIGNATURE Sig;
PNTLMSSP_MESSAGE_SIGNATURE pSig; // pointer to buffer with sig in it
NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // Aligned sig buffer.
UNREFERENCED_PARAMETER(pfQOP);
pContext = ReferenceUserContext(ContextHandle, FALSE);
if (!pContext)
{
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SpVerifySignature, ReferenceUserContext returns NULL\n" ));
goto CleanUp;
}
Status = SspSignSealHelper(
pContext,
eVerify,
pMessage,
MessageSeqNo,
&Sig,
&pSig
);
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SpVerifySignature, SspSignSealHelper returns %lx\n", Status ));
goto CleanUp;
}
RtlCopyMemory( &AlignedSig, pSig, sizeof( AlignedSig ) );
if (AlignedSig.Version != NTLM_SIGN_VERSION) {
SspPrint(( SSP_CRITICAL, "SpVerifySignature, unknown Version wanted=%lx got=%lx\n",
NTLM_SIGN_VERSION,
AlignedSig.Version
));
Status = SEC_E_INVALID_TOKEN;
goto CleanUp;
}
// validate the signature...
if (AlignedSig.CheckSum != Sig.CheckSum)
{
SspPrint(( SSP_CRITICAL, "SpVerifySignature, CheckSum mis-match wanted=%lx got=%lx\n",
Sig.CheckSum,
AlignedSig.CheckSum
));
Status = SEC_E_MESSAGE_ALTERED;
goto CleanUp;
}
// with MD5 sig, this now matters!
if (AlignedSig.RandomPad != Sig.RandomPad)
{
SspPrint(( SSP_CRITICAL, "SpVerifySignature, RandomPad mis-match wanted=%lx got=%lx\n",
Sig.RandomPad,
AlignedSig.RandomPad
));
Status = SEC_E_MESSAGE_ALTERED;
goto CleanUp;
}
if (AlignedSig.Nonce != Sig.Nonce)
{
SspPrint(( SSP_CRITICAL, "SpVerifySignature, Nonce mis-match wanted=%lx got=%lx\n",
Sig.Nonce,
AlignedSig.Nonce
));
Status = SEC_E_OUT_OF_SEQUENCE;
goto CleanUp;
}
CleanUp:
if (pContext != NULL)
{
SubStatus = DereferenceUserContext(pContext);
// Don't destroy real status
if (NT_SUCCESS(Status))
{
Status = SubStatus;
}
}
SspPrint(( SSP_API, "Leaving SpVerifySignature: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpSealMessage
//
// Synopsis: Verifies a signed message buffer by calculating a checksum over all
// the non-read only data buffers and encrypting the checksum
// along with a nonce.
//
// Effects:
//
// Arguments: ContextHandle - Handle of the context to use to sign the
// message.
// MessageBuffers - Contains an array of signed buffers and
// a signature buffer.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
// QualityOfProtection - Unused flags.
//
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found or was too small.
//
// Returns:
//
// Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
// routine SspHandleSealMessage. It's possible that
// bugs got copied too
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpSealMessage(
IN ULONG_PTR ContextHandle,
IN ULONG fQOP,
IN PSecBufferDesc pMessage,
IN ULONG MessageSeqNo
)
{
SspPrint(( SSP_API, "Entering SpSealMessage\n" ));
NTSTATUS Status = S_OK;
NTSTATUS SubStatus = S_OK;
PNTLM_CLIENT_CONTEXT pContext;
NTLMSSP_MESSAGE_SIGNATURE Sig;
PNTLMSSP_MESSAGE_SIGNATURE pSig; // pointer to buffer where sig goes
ULONG i;
UNREFERENCED_PARAMETER(fQOP);
pContext = ReferenceUserContext(ContextHandle, FALSE);
if (!pContext)
{
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SpSealMessage, ReferenceUserContext returns NULL\n" ));
goto CleanUp;
}
Status = SspSignSealHelper(
pContext,
eSeal,
pMessage,
MessageSeqNo,
&Sig,
&pSig
);
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SpVerifySignature, SspSignSealHelper returns %lx\n", Status ));
goto CleanUp;
}
RtlCopyMemory(
pSig,
&Sig,
NTLMSSP_MESSAGE_SIGNATURE_SIZE
);
//
// for gss style sign/seal, strip the padding as RC4 requires none.
// (in fact, we rely on this to simplify the size computation in DecryptMessage).
// if we support some other block cipher, need to rev the NTLM_ token version to make blocksize
//
for (i = 0; i < pMessage->cBuffers; i++)
{
if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_PADDING)
{
//
// no padding required!
//
pMessage->pBuffers[i].cbBuffer = 0;
break;
}
}
CleanUp:
if (pContext != NULL)
{
SubStatus = DereferenceUserContext(pContext);
// Don't destroy real status
if (NT_SUCCESS(Status))
{
Status = SubStatus;
}
}
SspPrint(( SSP_API, "Leaving SpSealMessage: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpUnsealMessage
//
// Synopsis: Verifies a signed message buffer by calculating a checksum over all
// the non-read only data buffers and encrypting the checksum
// along with a nonce.
//
// Effects:
//
// Arguments: ContextHandle - Handle of the context to use to sign the
// message.
// MessageBuffers - Contains an array of signed buffers and
// a signature buffer.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
// QualityOfProtection - Unused flags.
//
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found or was too small.
//
// Returns:
//
// Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
// routine SspHandleUnsealMessage. It's possible that
// bugs got copied too
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpUnsealMessage(
IN ULONG_PTR ContextHandle,
IN PSecBufferDesc pMessage,
IN ULONG MessageSeqNo,
OUT PULONG pfQOP
)
{
SspPrint(( SSP_API, "Entering SpUnsealMessage\n" ));
NTSTATUS Status = S_OK;
NTSTATUS SubStatus = S_OK;
PNTLM_CLIENT_CONTEXT pContext;
NTLMSSP_MESSAGE_SIGNATURE Sig;
PNTLMSSP_MESSAGE_SIGNATURE pSig; // pointer to buffer where sig goes
NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // aligned buffer.
PSecBufferDesc MessageBuffers = pMessage;
ULONG Index;
PSecBuffer SignatureBuffer = NULL;
PSecBuffer StreamBuffer = NULL;
PSecBuffer DataBuffer = NULL;
SecBufferDesc ProcessBuffers;
SecBuffer wrap_bufs[2];
UNREFERENCED_PARAMETER(pfQOP);
pContext = ReferenceUserContext(ContextHandle, FALSE);
if (!pContext)
{
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, ReferenceUserContext returns NULL\n" ));
goto CleanUp;
}
//
// Find the body and signature SecBuffers from pMessage
//
for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ )
{
if ((MessageBuffers->pBuffers[Index].BufferType & ~SECBUFFER_ATTRMASK) == SECBUFFER_TOKEN)
{
SignatureBuffer = &MessageBuffers->pBuffers[Index];
}
else if ((MessageBuffers->pBuffers[Index].BufferType & ~SECBUFFER_ATTRMASK) == SECBUFFER_STREAM)
{
StreamBuffer = &MessageBuffers->pBuffers[Index];
}
else if ((MessageBuffers->pBuffers[Index].BufferType & ~SECBUFFER_ATTRMASK) == SECBUFFER_DATA)
{
DataBuffer = &MessageBuffers->pBuffers[Index];
}
}
if( StreamBuffer != NULL )
{
if( SignatureBuffer != NULL )
{
Status = SEC_E_INVALID_TOKEN;
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, Both stream and signature buffer present.\n"));
goto CleanUp;
}
//
// for version 1 NTLM blobs, padding is never present, since RC4 is stream cipher.
//
wrap_bufs[0].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
wrap_bufs[1].cbBuffer = StreamBuffer->cbBuffer - NTLMSSP_MESSAGE_SIGNATURE_SIZE;
if( StreamBuffer->cbBuffer < wrap_bufs[0].cbBuffer )
{
Status = SEC_E_INVALID_TOKEN;
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, invalid buffer present in STREAM.\n"));
goto CleanUp;
}
wrap_bufs[0].BufferType = SECBUFFER_TOKEN;
wrap_bufs[0].pvBuffer = StreamBuffer->pvBuffer;
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].pvBuffer = (PBYTE)wrap_bufs[0].pvBuffer + wrap_bufs[0].cbBuffer;
if( DataBuffer == NULL )
{
Status = SEC_E_INVALID_TOKEN;
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, gss missing SECBUFFER_DATA.\n"));
goto CleanUp;
}
DataBuffer->cbBuffer = wrap_bufs[1].cbBuffer;
DataBuffer->pvBuffer = wrap_bufs[1].pvBuffer;
ProcessBuffers.cBuffers = 2;
ProcessBuffers.pBuffers = wrap_bufs;
ProcessBuffers.ulVersion = SECBUFFER_VERSION;
} else {
ProcessBuffers = *MessageBuffers;
}
Status = SspSignSealHelper(
pContext,
eUnseal,
&ProcessBuffers,
MessageSeqNo,
&Sig,
&pSig
);
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, SspSignSealHelper returns %lx\n", Status ));
goto CleanUp;
}
RtlCopyMemory( &AlignedSig, pSig, sizeof(AlignedSig) );
if (AlignedSig.Version != NTLM_SIGN_VERSION) {
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, unknown Version wanted=%lx got=%lx\n",
NTLM_SIGN_VERSION,
AlignedSig.Version
));
Status = SEC_E_INVALID_TOKEN;
goto CleanUp;
}
// validate the signature...
if (AlignedSig.CheckSum != Sig.CheckSum)
{
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, CheckSum mis-match wanted=%lx got=%lx\n",
Sig.CheckSum,
AlignedSig.CheckSum
));
Status = SEC_E_MESSAGE_ALTERED;
goto CleanUp;
}
if (AlignedSig.RandomPad != Sig.RandomPad)
{
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, RandomPad mis-match wanted=%lx got=%lx\n",
Sig.RandomPad,
AlignedSig.RandomPad
));
Status = SEC_E_MESSAGE_ALTERED;
goto CleanUp;
}
if (AlignedSig.Nonce != Sig.Nonce)
{
SspPrint(( SSP_CRITICAL, "SpUnsealMessage, Nonce mis-match wanted=%lx got=%lx\n",
Sig.Nonce,
AlignedSig.Nonce
));
Status = SEC_E_OUT_OF_SEQUENCE;
goto CleanUp;
}
CleanUp:
if (pContext != NULL)
{
SubStatus = DereferenceUserContext(pContext);
// Don't destroy real status
if (NT_SUCCESS(Status))
{
Status = SubStatus;
}
}
SspPrint(( SSP_API, "Leaving SpUnsealMessage: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpGetContextToken
//
// Synopsis: returns a pointer to the token for a server-side context
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpGetContextToken(
IN ULONG_PTR ContextHandle,
OUT PHANDLE ImpersonationToken
)
{
SspPrint(( SSP_API, "Entering SpGetContextToken\n" ));
NTSTATUS Status = S_OK;
PNTLM_CLIENT_CONTEXT pContext;
pContext = ReferenceUserContext(ContextHandle, FALSE);
if (pContext && pContext->ClientTokenHandle)
{
*ImpersonationToken = pContext->ClientTokenHandle;
Status= S_OK;
goto CleanUp;
}
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SpGetContextToken, no token handle\n" ));
CleanUp:
if (pContext != NULL)
{
Status = DereferenceUserContext(pContext);
}
SspPrint(( SSP_API, "Leaving SpGetContextToken: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpQueryContextAttributes
//
// 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_NOT_SUPPORTED -- Function code is not supported
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpQueryContextAttributes(
IN ULONG_PTR ContextHandle,
IN ULONG Attribute,
IN OUT PVOID Buffer
)
{
NTSTATUS Status = STATUS_SUCCESS;
PNTLM_CLIENT_CONTEXT pContext = NULL;
PSecPkgContext_Sizes ContextSizes;
PSecPkgContext_Flags ContextFlags;
PSecPkgContext_DceInfo ContextDceInfo = NULL;
PSecPkgContext_Names ContextNames = NULL;
PSecPkgContext_PackageInfo PackageInfo;
PSecPkgContext_NegotiationInfo NegInfo ;
PSecPkgContext_PasswordExpiry PasswordExpires;
PSecPkgContext_UserFlags UserFlags;
PSecPkgContext_SessionKey SessionKeyInfo;
PSecPkgContext_AccessToken AccessToken;
ULONG PackageInfoSize = 0;
SspPrint(( SSP_API, "Entering SpQueryContextAttributes\n" ));
pContext = ReferenceUserContext(ContextHandle, FALSE);
if (pContext == NULL) {
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_API_MORE, "SpQueryContextAttributes, ReferenceUserContext returns NULL (possible incomplete context)\n" ));
goto Cleanup;
}
//
// Handle each of the various queried attributes
//
SspPrint(( SSP_API_MORE, "SpQueryContextAttributes : 0x%lx\n", Attribute ));
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;
if((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) ||
(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
{
ContextSizes->cbSecurityTrailer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
} else {
ContextSizes->cbSecurityTrailer = 0;
}
}
break;
case SECPKG_ATTR_DCE_INFO:
ContextDceInfo = (PSecPkgContext_DceInfo) Buffer;
if (pContext->ContextNames)
{
UINT Length = wcslen(pContext->ContextNames);
ContextDceInfo->pPac = (LPWSTR)UserFunctions->AllocateHeap((Length + 1) * sizeof(WCHAR));
if (ContextDceInfo->pPac == NULL) {
Status = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL, "SpQueryContextAttributes, NtLmAllocate returns NULL\n" ));
goto Cleanup;
}
RtlCopyMemory(
(LPWSTR) ContextDceInfo->pPac,
pContext->ContextNames,
Length * sizeof(WCHAR)
);
*((LPWSTR)(ContextDceInfo->pPac) + Length) = L'\0';
}
else
{
SspPrint(( SSP_API_MORE, "SpQueryContextAttributes no ContextNames\n" ));
ContextDceInfo->pPac = (LPWSTR) UserFunctions->AllocateHeap(sizeof(WCHAR));
*((LPWSTR)(ContextDceInfo->pPac)) = L'\0';
}
ContextDceInfo->AuthzSvc = 0;
break;
case SECPKG_ATTR_SESSION_KEY:
{
if (NtLmState != NtLmLsaMode)
{
Status = STATUS_INVALID_PARAMETER;
break;
}
SessionKeyInfo = (PSecPkgContext_SessionKey) Buffer;
SessionKeyInfo->SessionKeyLength = sizeof(pContext->SessionKey);
SessionKeyInfo->SessionKey = (PUCHAR) UserFunctions->AllocateHeap(
SessionKeyInfo->SessionKeyLength
);
if (SessionKeyInfo->SessionKey != NULL)
{
RtlCopyMemory(
SessionKeyInfo->SessionKey,
pContext->SessionKey,
SessionKeyInfo->SessionKeyLength
);
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
break;
}
case SECPKG_ATTR_NAMES:
ContextNames = (PSecPkgContext_Names) Buffer;
if (pContext->ContextNames)
{
UINT Length = wcslen(pContext->ContextNames);
ContextNames->sUserName = (LPWSTR) UserFunctions->AllocateHeap((Length+1) * sizeof(WCHAR));
if (ContextNames->sUserName == NULL) {
Status = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL, "SpQueryContextAttributes, NtLmAllocate returns NULL\n" ));
goto Cleanup;
}
RtlCopyMemory(
ContextNames->sUserName,
pContext->ContextNames,
Length * sizeof(WCHAR)
);
*(ContextNames->sUserName + Length) = L'\0';
}
else
{
SspPrint(( SSP_API_MORE, "SpQueryContextAttributes no ContextNames\n" ));
ContextNames->sUserName = (LPWSTR) UserFunctions->AllocateHeap(sizeof(WCHAR));
*(ContextNames->sUserName) = L'\0';
}
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;
PackageInfoSize = sizeof(SecPkgInfoW) + sizeof(NTLMSP_NAME) + sizeof(NTLMSP_COMMENT);
PackageInfo->PackageInfo = (PSecPkgInfoW) UserFunctions->AllocateHeap(PackageInfoSize);
if (PackageInfo->PackageInfo == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
PackageInfo->PackageInfo->Name = (LPWSTR) (PackageInfo->PackageInfo + 1);
PackageInfo->PackageInfo->Comment = (LPWSTR) ((((PBYTE) 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 = RPC_C_AUTHN_WINNT;
PackageInfo->PackageInfo->fCapabilities = NTLMSP_CAPS;
PackageInfo->PackageInfo->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
if ( Attribute == SECPKG_ATTR_NEGOTIATION_INFO )
{
NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ;
NegInfo->NegotiationState = SECPKG_NEGOTIATION_COMPLETE ;
}
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;
//
// note: doesn't return all flags; by design.
//
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_ACCESS_TOKEN:
AccessToken = (PSecPkgContext_AccessToken) Buffer;
//
// ClientTokenHandle can be NULL, for instance:
// 1. client side context.
// 2. incomplete server context.
//
if(pContext->ClientTokenHandle)
AccessToken->AccessToken = pContext->ClientTokenHandle;
else
Status = SEC_E_NO_IMPERSONATION;
break;
case SECPKG_ATTR_LIFESPAN:
default:
Status = STATUS_NOT_SUPPORTED;
break;
}
Cleanup:
if (!NT_SUCCESS(Status))
{
switch (Attribute) {
case SECPKG_ATTR_NAMES:
if (ContextNames != NULL && ContextNames->sUserName )
{
NtLmFree(ContextNames->sUserName);
}
break;
case SECPKG_ATTR_DCE_INFO:
if (ContextDceInfo != NULL && ContextDceInfo->pPac)
{
NtLmFree(ContextDceInfo->pPac);
}
break;
}
}
if (pContext != NULL)
{
(VOID) DereferenceUserContext(pContext);
}
SspPrint(( SSP_API, "Leaving SpQueryContextAttributes: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpCompleteAuthToken
//
// Synopsis: Completes a context
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpCompleteAuthToken(
IN ULONG_PTR ContextHandle,
IN PSecBufferDesc InputBuffer
)
{
UNREFERENCED_PARAMETER (ContextHandle);
UNREFERENCED_PARAMETER (InputBuffer);
SspPrint(( SSP_API, "Entering SpCompleteAuthToken\n" ));
SspPrint(( SSP_API, "Leaving SpCompleteAuthToken\n" ));
return(SEC_E_UNSUPPORTED_FUNCTION);
}
NTSTATUS NTAPI
SpFormatCredentials(
IN PSecBuffer Credentials,
OUT PSecBuffer FormattedCredentials
)
{
UNREFERENCED_PARAMETER (Credentials);
UNREFERENCED_PARAMETER (FormattedCredentials);
SspPrint(( SSP_API, "Entering SpFormatCredentials\n" ));
SspPrint(( SSP_API, "Leaving SpFormatCredentials\n" ));
return(SEC_E_UNSUPPORTED_FUNCTION);
}
NTSTATUS NTAPI
SpMarshallSupplementalCreds(
IN ULONG CredentialSize,
IN PUCHAR Credentials,
OUT PULONG MarshalledCredSize,
OUT PVOID * MarshalledCreds
)
{
UNREFERENCED_PARAMETER (CredentialSize);
UNREFERENCED_PARAMETER (Credentials);
UNREFERENCED_PARAMETER (MarshalledCredSize);
UNREFERENCED_PARAMETER (MarshalledCreds);
SspPrint(( SSP_API, "Entering SpMarshallSupplementalCreds\n" ));
SspPrint(( SSP_API, "Leaving SpMarshallSupplementalCreds\n" ));
return(SEC_E_UNSUPPORTED_FUNCTION);
}
//+-------------------------------------------------------------------------
//
// 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_CLIENT_CONTEXT Context,
OUT PBOOLEAN MappedContext,
OUT PSecBuffer ContextData,
IN ULONG Flags
)
{
NTSTATUS Status = STATUS_SUCCESS;
PNTLM_PACKED_CONTEXT PackedContext = NULL;
ULONG ContextSize, ContextNameSize = 0;
if (Context->ContextNames)
{
ContextNameSize = wcslen(Context->ContextNames);
}
ContextSize = sizeof(NTLM_CLIENT_CONTEXT) +
ContextNameSize * sizeof(WCHAR) + sizeof( WCHAR );
PackedContext = (PNTLM_PACKED_CONTEXT) NtLmAllocateLsaHeap( ContextSize );
if (PackedContext == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
// Copy all fields of the old context
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;
ContextData->pvBuffer = PackedContext;
ContextData->cbBuffer = ContextSize;
*MappedContext = TRUE;
Status = STATUS_SUCCESS;
Cleanup:
if (!NT_SUCCESS(Status))
{
if (PackedContext != NULL)
{
NtLmFreeLsaHeap(PackedContext);
}
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: SpExportSecurityContext
//
// Synopsis: Exports a security context to another process
//
// Effects: Allocates memory for output
//
// Arguments: ContextHandle - handle to context to export
// Flags - Flags concerning duplication. Allowable flags:
// SECPKG_CONTEXT_EXPORT_DELETE_OLD - causes old context
// to be deleted.
// PackedContext - Receives serialized context to be freed with
// FreeContextBuffer
// TokenHandle - Optionally receives handle to context's token.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
SpExportSecurityContext(
IN ULONG_PTR ContextHandle,
IN ULONG Flags,
OUT PSecBuffer PackedContext,
OUT PHANDLE TokenHandle
)
{
PNTLM_CLIENT_CONTEXT Context = NULL ;
PNTLM_PACKED_CONTEXT pvContext = NULL;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS SubStatus = STATUS_SUCCESS;
ULONG ContextSize = 0;
BOOLEAN MappedContext = FALSE;
SspPrint(( SSP_API,"Entering SpExportSecurityContext for context 0x%x\n", ContextHandle ));
if (ARGUMENT_PRESENT(TokenHandle))
{
*TokenHandle = NULL;
}
PackedContext->pvBuffer = NULL;
PackedContext->cbBuffer = 0;
PackedContext->BufferType = 0;
Context = ReferenceUserContext(
ContextHandle,
FALSE // don't unlink
);
if (Context == NULL)
{
SspPrint((SSP_CRITICAL, "SpExportSecurityContext: Invalid handle supplied (0x%x)\n",
ContextHandle));
Status = STATUS_INVALID_HANDLE;
goto Cleanup;
}
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))
{
ULONG LockIndex;
LockIndex = ListIndexToLockIndex( HandleToListIndex( ContextHandle ) );
RtlAcquireResourceShared( &NtLmUserContextLock[LockIndex], TRUE );
if ((Flags & SECPKG_CONTEXT_EXPORT_DELETE_OLD) != 0)
{
RtlConvertSharedToExclusive( &NtLmUserContextLock[LockIndex] );
*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
);
}
RtlReleaseResource( &NtLmUserContextLock[LockIndex] );
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
pvContext = (PNTLM_PACKED_CONTEXT) PackedContext->pvBuffer;
// Semantics of this flag: Export from here, but reset the Nonce.
// We zero out the session key, since all we need is the rc4 key.
if ((Flags & SECPKG_CONTEXT_EXPORT_RESET_NEW) != 0)
{
pvContext->SendNonce = (ULONG) -1;
pvContext->RecvNonce = (ULONG) -1;
}
RtlZeroMemory(
&pvContext->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH
);
Cleanup:
if (Context != NULL)
{
SubStatus = DereferenceUserContext(Context);
// Don't destroy real status
if (NT_SUCCESS(Status))
{
Status = SubStatus;
}
}
SspPrint(( SSP_API,"Leaving SpExportContext: 0x%lx\n", Status ));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: NtLmCreateUserModeContext
//
// Synopsis: Creates a user-mode context to support impersonation and
// message integrity and privacy
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
NtLmCreateUserModeContext(
IN ULONG_PTR ContextHandle,
IN HANDLE Token,
IN PSecBuffer MarshalledContext,
OUT PNTLM_CLIENT_CONTEXT * NewContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
PNTLM_CLIENT_CONTEXT Context = NULL;
PNTLM_PACKED_CONTEXT PackedContext;
UINT Length = 0;
ULONG ListIndex;
ULONG LockIndex;
if (MarshalledContext->cbBuffer < sizeof(NTLM_PACKED_CONTEXT))
{
SspPrint((SSP_CRITICAL,"NtLmCreateUserModeContext: 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_CLIENT_CONTEXT)NtLmAllocate ( sizeof(NTLM_CLIENT_CONTEXT));
if (Context == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Context->NegotiateFlags = PackedContext->NegotiateFlags ;
Context->SendNonce = PackedContext->SendNonce ;
Context->RecvNonce = PackedContext->RecvNonce ;
RtlCopyMemory(
Context->SessionKey,
PackedContext->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH );
Context->ContextSignature = PackedContext->ContextSignature ;
Context->PasswordExpiry = PackedContext->PasswordExpiry ;
Context->UserFlags = PackedContext->UserFlags ;
if ( PackedContext->ContextNames )
{
Context->ContextNames = (PWSTR) NtLmAllocate( PackedContext->ContextNameLength );
if ( Context->ContextNames == NULL )
{
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto Cleanup ;
}
RtlCopyMemory(
Context->ContextNames,
((PUCHAR) PackedContext) + PackedContext->ContextNames,
PackedContext->ContextNameLength );
}
else
{
Context->ContextNames = NULL ;
}
RtlCopyMemory(
Context->SignSessionKey,
PackedContext->SignSessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH );
RtlCopyMemory(
Context->VerifySessionKey,
PackedContext->VerifySessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH );
RtlCopyMemory(
Context->SealSessionKey,
PackedContext->SealSessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH );
RtlCopyMemory(
Context->UnsealSessionKey,
PackedContext->UnsealSessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH );
RtlCopyMemory(
&Context->SealRc4Sched,
&PackedContext->SealRc4Sched,
sizeof( struct RC4_KEYSTRUCT ) );
RtlCopyMemory(
&Context->UnsealRc4Sched,
&PackedContext->UnsealRc4Sched,
sizeof( struct RC4_KEYSTRUCT ) );
// These need to be changed
Context->ClientTokenHandle = Token;
if (Context->SendNonce == (ULONG) -1)
{
Context->SendNonce = 0;
}
if (Context->RecvNonce == (ULONG) -1)
{
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->References = 2;
//
// Modify the DACL on the token to grant access to the caller
//
if (Context->ClientTokenHandle != NULL)
{
Status = SspCreateTokenDacl(
Context->ClientTokenHandle
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
// Dummy up an lsa handle by incrementing a global variable. This
// will ensure that each imported context has a unique handle.
// Skip over values that could be interpreted as an aligned pointer,
// so that they won't get mixed up with real lsa handles.
Context->LsaContext = InterlockedIncrement((PLONG)&ExportedContext);
while(Context->LsaContext % MAX_NATURAL_ALIGNMENT == 0)
{
Context->LsaContext = InterlockedIncrement((PLONG)&ExportedContext);
}
ListIndex = HandleToListIndex( Context->LsaContext );
LockIndex = ListIndexToLockIndex( ListIndex );
RtlAcquireResourceExclusive(&NtLmUserContextLock[LockIndex], TRUE);
InsertHeadList ( &NtLmUserContextList[ListIndex], &Context->Next );
NtLmUserContextCount[ListIndex]++;
RtlReleaseResource(&NtLmUserContextLock[LockIndex]);
*NewContext = Context;
Cleanup:
if (!NT_SUCCESS(Status))
{
if (Context != NULL)
{
FreeUserContext(Context);
}
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: SpImportSecurityContext
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
SpImportSecurityContext(
IN PSecBuffer PackedContext,
IN HANDLE Token,
OUT PULONG_PTR ContextHandle
)
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS SubStatus = STATUS_SUCCESS;
PNTLM_CLIENT_CONTEXT Context = NULL;
SspPrint(( SSP_API,"Entering SpImportSecurityContext\n"));
Status = NtLmCreateUserModeContext(
0, // no lsa context
Token,
PackedContext,
&Context
);
if (!NT_SUCCESS(Status))
{
SspPrint((SSP_CRITICAL,"SpImportSecurityContext: Failed to create user mode context: 0x%x\n",
Status));
goto Cleanup;
}
*ContextHandle = Context->LsaContext;
Cleanup:
if (Context != NULL)
{
SubStatus = DereferenceUserContext(Context);
// Don't destroy real status
if (NT_SUCCESS(Status))
{
Status = SubStatus;
}
}
SspPrint(( SSP_API,"Leaving SpImportSecurityContext: 0x%lx\n", Status));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
NTSTATUS
SspGetTokenUser(
HANDLE Token,
PTOKEN_USER pTokenUser,
PULONG TokenUserSize
)
/*++
RoutineDescription:
Gets the TOKEN_USER from an open token
Arguments:
Token - Handle to a token open for TOKEN_QUERY access
Return Value:
STATUS_INSUFFICIENT_RESOURCES - not enough memory to complete the
function.
Errors from NtQueryInformationToken.
--*/
{
NTSTATUS Status;
Status = NtQueryInformationToken(
Token,
TokenUser,
pTokenUser,
*TokenUserSize,
TokenUserSize
);
return(Status);
}
NTSTATUS
SspCreateTokenDacl(
HANDLE Token
)
/*++
RoutineDescription:
Creates a new DACL for the token granting the server and client
all access to the token.
Arguments:
Token - Handle to an impersonation token open for TOKEN_QUERY and
WRITE_DAC
Return Value:
STATUS_INSUFFICIENT_RESOURCES - insufficient memory to complete
the function.
Errors from NtSetSecurityObject
--*/
{
NTSTATUS Status;
PTOKEN_USER ThreadTokenUser;
PTOKEN_USER ImpersonationTokenUser = NULL;
PTOKEN_USER SlowProcessTokenUser = NULL;
PTOKEN_USER SlowThreadTokenUser = NULL;
PTOKEN_USER SlowImpersonationTokenUser = NULL;
ULONG_PTR FastThreadTokenUser[ 128/sizeof(ULONG_PTR) ];
ULONG_PTR FastImpersonationTokenUser[ 128/sizeof(ULONG_PTR) ];
ULONG TokenUserSize;
HANDLE ProcessToken = NULL;
HANDLE ImpersonationToken = NULL;
BOOL fInsertImpersonatingUser = FALSE;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
ULONG AclLength;
PACL NewDacl;
PACL SlowNewDacl = NULL;
ULONG_PTR FastNewDacl[ 512/sizeof(ULONG_PTR) ];
SECURITY_DESCRIPTOR SecurityDescriptor;
//
// Build the two well known sids we need.
//
if (NtLmGlobalLocalSystemSid == NULL)
{
PSID pLocalSidSystem;
PSID pOldSid;
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,0,0,0,0,0,0,
&pLocalSidSystem
);
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, RtlAllocateAndInitializeSid returns 0x%lx\n", Status ));
goto Cleanup;
}
pOldSid = InterlockedCompareExchangePointer(
&NtLmGlobalLocalSystemSid,
pLocalSidSystem,
NULL
);
if( pOldSid )
{
RtlFreeSid( pLocalSidSystem );
}
}
if (NtLmGlobalAliasAdminsSid == NULL)
{
PSID pLocalSidAdmins;
PSID pOldSid;
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0,
&pLocalSidAdmins
);
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, RtlAllocateAndInitializeSid returns 0x%lx\n", Status ));
goto Cleanup;
}
pOldSid = InterlockedCompareExchangePointer(
&NtLmGlobalAliasAdminsSid,
pLocalSidAdmins,
NULL
);
if( pOldSid )
{
RtlFreeSid( pLocalSidAdmins );
}
}
//
// it's possible that the current thread is impersonating a user.
// if that's the case, get it's token user, and revert to insure we
// can open the process token.
//
Status = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_QUERY | TOKEN_IMPERSONATE,
TRUE,
&ImpersonationToken
);
if( NT_SUCCESS(Status) )
{
//
// stop impersonating.
//
RevertToSelf();
//
// get the token user for the impersonating user.
//
ImpersonationTokenUser = (PTOKEN_USER)FastImpersonationTokenUser;
TokenUserSize = sizeof(FastImpersonationTokenUser);
Status = SspGetTokenUser(
ImpersonationToken,
ImpersonationTokenUser,
&TokenUserSize
);
if( Status == STATUS_BUFFER_TOO_SMALL )
{
SlowImpersonationTokenUser = (PTOKEN_USER)NtLmAllocate( TokenUserSize );
if(SlowImpersonationTokenUser == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
ImpersonationTokenUser = SlowImpersonationTokenUser;
Status = SspGetTokenUser(
ImpersonationToken,
ImpersonationTokenUser,
&TokenUserSize
);
}
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, SspGetTokenUser returns 0x%lx\n", Status ));
goto Cleanup;
}
}
if( NtLmGlobalProcessUserSid == NULL )
{
PTOKEN_USER ProcessTokenUser;
ULONG_PTR FastProcessTokenUser[ 128/sizeof(ULONG_PTR) ];
PSID pOldSid;
PSID pNewSid;
ULONG cbNewSid;
//
// Open the process token to find out the user sid
//
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&ProcessToken
);
if(!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, NtOpenProcessToken returns 0x%lx\n", Status ));
goto Cleanup;
}
//
// get the token user for the process token.
//
ProcessTokenUser = (PTOKEN_USER)FastProcessTokenUser;
TokenUserSize = sizeof(FastProcessTokenUser);
Status = SspGetTokenUser(
ProcessToken,
ProcessTokenUser,
&TokenUserSize
);
if( Status == STATUS_BUFFER_TOO_SMALL )
{
SlowProcessTokenUser = (PTOKEN_USER)NtLmAllocate( TokenUserSize );
if(SlowProcessTokenUser == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
ProcessTokenUser = SlowProcessTokenUser;
Status = SspGetTokenUser(
ProcessToken,
ProcessTokenUser,
&TokenUserSize
);
}
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, SspGetTokenUser returns 0x%lx\n", Status ));
goto Cleanup;
}
cbNewSid = RtlLengthSid( ProcessTokenUser->User.Sid );
pNewSid = NtLmAllocate( cbNewSid );
if( pNewSid == NULL )
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
CopyMemory( pNewSid, ProcessTokenUser->User.Sid, cbNewSid );
pOldSid = InterlockedCompareExchangePointer(
&NtLmGlobalProcessUserSid,
pNewSid,
NULL
);
if( pOldSid )
{
NtLmFree( pNewSid );
}
}
//
// Now get the token user for the thread.
//
ThreadTokenUser = (PTOKEN_USER)FastThreadTokenUser;
TokenUserSize = sizeof(FastThreadTokenUser);
Status = SspGetTokenUser(
Token,
ThreadTokenUser,
&TokenUserSize
);
if( Status == STATUS_BUFFER_TOO_SMALL )
{
SlowThreadTokenUser = (PTOKEN_USER)NtLmAllocate( TokenUserSize );
if(SlowThreadTokenUser == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
ThreadTokenUser = SlowThreadTokenUser;
Status = SspGetTokenUser(
Token,
ThreadTokenUser,
&TokenUserSize
);
}
if (!NT_SUCCESS(Status))
{
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, SspGetTokenUser returns 0x%lx\n", Status ));
goto Cleanup;
}
AclLength = 4 * sizeof( ACCESS_ALLOWED_ACE ) - 4 * sizeof( ULONG ) +
RtlLengthSid( NtLmGlobalProcessUserSid ) +
RtlLengthSid( ThreadTokenUser->User.Sid ) +
RtlLengthSid( NtLmGlobalLocalSystemSid ) +
RtlLengthSid( NtLmGlobalAliasAdminsSid ) +
sizeof( ACL );
//
// determine if we need to add impersonation token sid onto the token Dacl.
//
if( ImpersonationTokenUser &&
!RtlEqualSid( ImpersonationTokenUser->User.Sid, NtLmGlobalProcessUserSid ) &&
!RtlEqualSid( ImpersonationTokenUser->User.Sid, ThreadTokenUser->User.Sid )
)
{
AclLength += (sizeof(ACCESS_ALLOWED_ACE) - sizeof( ULONG )) +
RtlLengthSid( ImpersonationTokenUser->User.Sid );
fInsertImpersonatingUser = TRUE;
}
if( AclLength <= sizeof(FastNewDacl) )
{
NewDacl = (PACL)FastNewDacl;
} else {
SlowNewDacl = (PACL)NtLmAllocate(AclLength );
NewDacl = SlowNewDacl;
if (SlowNewDacl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
SspPrint(( SSP_CRITICAL, "SspCreateTokenDacl, NtLmallocate returns 0x%lx\n", NewDacl));
goto Cleanup;
}
}
Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
ASSERT(NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
NtLmGlobalProcessUserSid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
ThreadTokenUser->User.Sid
);
ASSERT( NT_SUCCESS( Status ));
if( fInsertImpersonatingUser )
{
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
ImpersonationTokenUser->User.Sid
);
ASSERT( NT_SUCCESS( Status ));
}
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
NtLmGlobalAliasAdminsSid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
NtLmGlobalLocalSystemSid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlCreateSecurityDescriptor (
&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlSetDaclSecurityDescriptor(
&SecurityDescriptor,
TRUE,
NewDacl,
FALSE
);
ASSERT( NT_SUCCESS( Status ));
Status = NtSetSecurityObject(
Token,
DACL_SECURITY_INFORMATION,
&SecurityDescriptor
);
ASSERT( NT_SUCCESS( Status ));
Cleanup:
if (ImpersonationToken != NULL)
{
//
// put the thread token back if we were impersonating.
//
SetThreadToken( NULL, ImpersonationToken );
NtClose(ImpersonationToken);
}
if (SlowThreadTokenUser != NULL) {
NtLmFree( SlowThreadTokenUser );
}
if (SlowProcessTokenUser != NULL) {
NtLmFree( SlowProcessTokenUser );
}
if (SlowImpersonationTokenUser != NULL) {
NtLmFree( SlowImpersonationTokenUser );
}
if (SlowNewDacl != NULL) {
NtLmFree( SlowNewDacl );
}
if (ProcessToken != NULL)
{
NtClose(ProcessToken);
}
return( Status );
}
NTSTATUS
SspMapContext(
IN PULONG_PTR phContext,
IN PUCHAR pSessionKey,
IN ULONG NegotiateFlags,
IN HANDLE TokenHandle,
IN PTimeStamp PasswordExpiry OPTIONAL,
IN ULONG UserFlags,
OUT PSecBuffer ContextData
)
/*++
RoutineDescription:
Create a local context for a real context
Don't link it to out list of local contexts.
Arguments:
Return Value:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PNTLM_PACKED_CONTEXT pContext = NULL;
ULONG cbContextData;
WCHAR ContextNames[(UNLEN + DNS_MAX_NAME_LENGTH + 2 + 1) *sizeof (WCHAR)];
PLUID pLogonId = NULL;
TOKEN_STATISTICS TokenStats;
PSSP_CONTEXT pTempContext = (PSSP_CONTEXT)*phContext;
PACTIVE_LOGON *ActiveLogon = NULL, Logon = NULL;
LPWSTR UserName = NULL;
UINT Length = 0;
BOOLEAN ActiveLogonsAreLocked = FALSE;
if (pTempContext == NULL)
{
Status = STATUS_INVALID_HANDLE;
SspPrint(( SSP_CRITICAL, "SspMapContext, pTempContext is 0x%lx\n", pTempContext));
goto Cleanup;
}
if( pTempContext->Credential != NULL ) {
pLogonId = &(pTempContext->Credential->LogonId);
} else {
//
// if it was a local call where the default creds were used, lookup
// the username and domain name based on the AuthenticationId of the
// access token. The local call gets a duplicated access token
// associated with the original client, so information on this logon
// should be found in the active logon list.
//
if( NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL &&
pTempContext->UserName.Length == 0 &&
pTempContext->UserName.Buffer == NULL &&
pTempContext->DomainName.Length == 0 &&
pTempContext->DomainName.Buffer == NULL &&
TokenHandle ) {
DWORD TokenInfoLength = sizeof( TokenStats );
if( GetTokenInformation(
TokenHandle,
TokenStatistics,
&TokenStats,
TokenInfoLength,
&TokenInfoLength
)) {
pLogonId = &(TokenStats.AuthenticationId);
}
}
}
if( pLogonId )
{
NlpLockActiveLogonsRead();
ActiveLogonsAreLocked = TRUE;
if (!NlpFindActiveLogon (
pLogonId,
&ActiveLogon))
{
// Status = STATUS_NO_SUCH_LOGON_SESSION;
SspPrint(( SSP_API_MORE, "SspMapContext, NlpFindActiveLogon returns FALSE\n"));
// SspPrint(( SSP_CRITICAL, "SspMapContext, NlpFindActiveLogon returns FALSE\n"));
// goto Cleanup;
}
}
if (ActiveLogon != NULL)
{
Logon = *ActiveLogon;
}
ContextNames[0] = L'\0';
if (Logon != NULL)
{
if ( (Logon->UserName.Length > 0) &&
(Logon->LogonDomainName.Length > 0))
{
RtlCopyMemory (ContextNames,
Logon->LogonDomainName.Buffer,
Logon->LogonDomainName.Length);
Length = (Logon->LogonDomainName.Length)/sizeof(WCHAR);
ContextNames[Length] = L'\\';
Length += 1;
UserName = &ContextNames[Length];
RtlCopyMemory (UserName,
Logon->UserName.Buffer,
Logon->UserName.Length);
Length += (Logon->UserName.Length)/sizeof(WCHAR);
ContextNames[Length] = L'\0';
}
}
else
{
// We don't store network logons, but in the case of server side
// mapping, the client has sent us the Context Names, so use that.
// Also, handle the case where we don't have domainnames, just usernames
// Must handle the valid case where both domainname & username are NULL (rdr)
if ((pTempContext->DomainName.Length > 0) &&
(pTempContext->UserName.Length > 0))
{
RtlCopyMemory (ContextNames,
pTempContext->DomainName.Buffer,
pTempContext->DomainName.Length);
Length = (pTempContext->DomainName.Length)/sizeof(WCHAR);
ContextNames[Length] = L'\\';
Length += 1;
UserName = &ContextNames[Length];
RtlCopyMemory (UserName,
pTempContext->UserName.Buffer,
pTempContext->UserName.Length);
Length += (pTempContext->UserName.Length)/sizeof(WCHAR);
ContextNames[Length] = L'\0';
}
else if ((pTempContext->DomainName.Length == 0) &&
(pTempContext->UserName.Length >0))
{
RtlCopyMemory (ContextNames + Length,
pTempContext->UserName.Buffer,
pTempContext->UserName.Length);
Length = (pTempContext->UserName.Length)/sizeof(WCHAR);
ContextNames[Length] = L'\0';
}
}
//
// when domain is present, don't supply domain\UPN, supply domain\user
//
if( UserName ) {
DWORD cchUserName = wcslen(UserName);
DWORD i;
for( i = 0 ; i < cchUserName ; i++ ) {
if( UserName[ i ] == L'@' ) {
UserName[ i ] = L'\0';
break;
}
}
}
Length = wcslen(ContextNames) * sizeof(WCHAR);
cbContextData =
sizeof(NTLM_PACKED_CONTEXT) +
Length +
sizeof(WCHAR);
cbContextData += pTempContext->cbMarshalledTargetInfo;
// the first sizeof (NTLM_CLIENT_CONTEXT) bytes can be
// casted to pContext anyway.
pContext = (PNTLM_PACKED_CONTEXT)NtLmAllocateLsaHeap( cbContextData );
if (!pContext)
{
SspPrint(( SSP_CRITICAL, "SspMapContext, NtLmAllocate returns NULL\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
// ZeroMemory( pContext, cbContextData );
pContext->NegotiateFlags = NegotiateFlags;
RtlCopyMemory( pContext->SessionKey,
pSessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH);
// Save away the LsaContextHandle
// pContext->LsaContext = *phContext;
// dup token if it exists
pContext->ClientTokenHandle = 0 ;
if (TokenHandle != NULL)
{
HANDLE Tmp ;
Status = LsaFunctions->DuplicateHandle(
TokenHandle,
&Tmp );
if (!NT_SUCCESS(Status))
{
if (pContext)
{
NtLmFreeLsaHeap(pContext);
}
SspPrint(( SSP_CRITICAL, "SspMapContext, DuplicateHandle returns 0x%lx\n", Status));
goto Cleanup;
}
pContext->ClientTokenHandle = (ULONG) ((ULONG_PTR) Tmp) ;
}
if (cbContextData > sizeof(NTLM_PACKED_CONTEXT) )
{
pContext->ContextNames = sizeof(NTLM_PACKED_CONTEXT);
pContext->ContextNameLength = Length;
RtlCopyMemory(pContext+1,
ContextNames,
pContext->ContextNameLength
);
pContext->ContextNameLength += sizeof(WCHAR);
}
else
{
pContext->ContextNames = 0;
pContext->ContextNameLength = 0;
}
if( pTempContext->pbMarshalledTargetInfo )
{
pContext->MarshalledTargetInfo = (ULONG)pContext->ContextNameLength + sizeof(NTLM_PACKED_CONTEXT) ;
pContext->MarshalledTargetInfoLength = pTempContext->cbMarshalledTargetInfo;
RtlCopyMemory( (PBYTE)pContext+pContext->MarshalledTargetInfo,
pTempContext->pbMarshalledTargetInfo,
pContext->MarshalledTargetInfoLength
);
}
pContext->SendNonce = 0;
pContext->RecvNonce = 0;
ContextData->pvBuffer = pContext;
ContextData->cbBuffer = cbContextData;
if(ARGUMENT_PRESENT(PasswordExpiry)) {
pContext->PasswordExpiry = *PasswordExpiry;
} else {
pContext->PasswordExpiry.QuadPart = 0;
}
pContext->UserFlags = UserFlags;
Cleanup:
if (ActiveLogonsAreLocked)
{
NlpUnlockActiveLogons();
}
return(Status);
}
ULONG
HandleToListIndex(
ULONG_PTR ContextHandle
)
{
ASSERT( (NTLM_USERLIST_COUNT != 0) );
ASSERT( (NTLM_USERLIST_COUNT & 1) == 0 );
ULONG Number ;
ULONG Hash;
ULONG HashFinal;
Number = (ULONG)ContextHandle;
Hash = Number;
Hash += Number >> 8;
Hash += Number >> 16;
Hash += Number >> 24;
HashFinal = Hash;
HashFinal += Hash >> 4;
//
// insure power of two if not one.
//
return ( HashFinal & (NTLM_USERLIST_COUNT-1) ) ;
}
ULONG
__inline
ListIndexToLockIndex(
ULONG ListIndex
)
{
ASSERT( (NTLM_USERLIST_LOCK_COUNT) != 0 );
ASSERT( (NTLM_USERLIST_LOCK_COUNT & 1) == 0 );
//
// insure power of two if not one.
//
return ( ListIndex & (NTLM_USERLIST_LOCK_COUNT-1) );
}