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.
1414 lines
35 KiB
1414 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
context.cxx
|
|
|
|
Abstract:
|
|
|
|
API and support routines for handling security contexts.
|
|
|
|
Author:
|
|
|
|
Cliff Van Dyke (CliffV) 13-Jul-1993
|
|
|
|
Revision History:
|
|
ChandanS 03-Aug-1996 Stolen from net\svcdlls\ntlmssp\common\context.c
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include <global.h>
|
|
#include <align.h> // ALIGN_WCHAR, etc
|
|
|
|
#include <lm.h>
|
|
#include <dsgetdc.h>
|
|
|
|
|
|
|
|
SECURITY_STATUS
|
|
SspContextReferenceContext(
|
|
IN ULONG_PTR ContextHandle,
|
|
IN BOOLEAN RemoveContext,
|
|
OUT PSSP_CONTEXT *ContextResult
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the Context is for the specified
|
|
Client Connection, and references the Context if it is valid.
|
|
|
|
The caller may optionally request that the Context be
|
|
removed from the list of valid Contexts - preventing future
|
|
requests from finding this Context.
|
|
|
|
Arguments:
|
|
|
|
ContextHandle - Points to the ContextHandle of the Context
|
|
to be referenced.
|
|
|
|
RemoveContext - This boolean value indicates whether the caller
|
|
wants the Context to be removed from the list
|
|
of Contexts. TRUE indicates the Context is to be removed.
|
|
FALSE indicates the Context is not to be removed.
|
|
|
|
ContextResult set to result context handle if successful.
|
|
|
|
|
|
Return Value:
|
|
|
|
SEC_E_OK returns a pointer to the referenced Context.
|
|
|
|
SEC_E_INVALID_HANDLE - invalid handle supplied
|
|
SEC_E_CONTEXT_EXPIRED - handle was valid, but expired
|
|
|
|
--*/
|
|
|
|
{
|
|
PSSP_CONTEXT Context;
|
|
|
|
SspPrint(( SSP_API_MORE, "Entering SspContextReferenceContext\n" ));
|
|
|
|
*ContextResult = NULL;
|
|
|
|
#if DBG
|
|
|
|
|
|
//
|
|
// check for leaky client applications.
|
|
//
|
|
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
if( LsaFunctions->GetCallInfo(&CallInfo) )
|
|
{
|
|
if ((CallInfo.Attributes & SECPKG_CALL_CLEANUP) != 0)
|
|
{
|
|
SspPrint(( SSP_LEAK_TRACK, "SspContextReferenceContext: pid: 0x%lx handle: %p refcount: %lu\n",
|
|
CallInfo.ProcessId, ContextHandle, CallInfo.CallCount));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Context = (PSSP_CONTEXT)ContextHandle;
|
|
|
|
__try {
|
|
|
|
if ( (Context->ContextTag != SSP_CONTEXT_TAG_ACTIVE)
|
|
|| (MSV1_0_USER_SESSION_KEY_LENGTH != RtlCompareMemory(Context->ContextMagicNumber, NtlmGlobalMagicNumber, MSV1_0_USER_SESSION_KEY_LENGTH)) )
|
|
{
|
|
SspPrint(( SSP_CRITICAL, "Tried to reference unknown Context %p\n",
|
|
Context ));
|
|
return SEC_E_INVALID_HANDLE;
|
|
}
|
|
|
|
#if 0
|
|
ASSERT( (KernelCaller == Context->KernelClient) );
|
|
#endif
|
|
|
|
if (!RemoveContext)
|
|
{
|
|
if (SspTimeHasElapsed(Context->TickStart, Context->Interval))
|
|
{
|
|
if ((Context->State != AuthenticatedState) &&
|
|
(Context->State != AuthenticateSentState) &&
|
|
(Context->State != PassedToServiceState))
|
|
{
|
|
|
|
SspPrint(( SSP_CRITICAL, "Context %p has timed out.\n",
|
|
ContextHandle ));
|
|
|
|
return SEC_E_CONTEXT_EXPIRED;
|
|
}
|
|
}
|
|
|
|
InterlockedIncrement( (PLONG)&Context->References );
|
|
}
|
|
else
|
|
{
|
|
Context->ContextTag = SSP_CONTEXT_TAG_DELETE;
|
|
|
|
RtlZeroMemory(Context->ContextMagicNumber, MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
|
|
SspPrint(( SSP_API_MORE, "Delinked Context %p\n", Context ));
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SspPrint(( SSP_CRITICAL, "Tried to reference invalid Context %p\n",
|
|
Context ));
|
|
return SEC_E_INVALID_HANDLE;
|
|
}
|
|
|
|
*ContextResult = Context;
|
|
return SEC_E_OK;
|
|
}
|
|
|
|
VOID
|
|
SspContextDereferenceContext(
|
|
PSSP_CONTEXT Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the specified Context's reference count.
|
|
If the reference count drops to zero, then the Context is deleted
|
|
|
|
Arguments:
|
|
|
|
Context - Points to the Context to be dereferenced.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LONG References;
|
|
|
|
SspPrint(( SSP_API_MORE, "Entering SspContextDereferenceContext\n" ));
|
|
|
|
//
|
|
// Decrement the reference count
|
|
//
|
|
|
|
References = InterlockedDecrement( (PLONG)&Context->References );
|
|
ASSERT( References >= 0 );
|
|
|
|
|
|
//
|
|
// If the count dropped to zero, then run-down the Context
|
|
//
|
|
|
|
if (References != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SspPrint(( SSP_API_MORE, "Deleting Context 0x%lx\n",
|
|
Context ));
|
|
|
|
Context->ContextTag = SSP_CONTEXT_TAG_DELETE;
|
|
RtlZeroMemory(Context->ContextMagicNumber, MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
|
|
if ( Context->Password.Buffer != NULL ) {
|
|
// note: Password.Length may contain run-encoding hint, so size may be illegal.
|
|
ZeroMemory( Context->Password.Buffer, Context->Password.MaximumLength );
|
|
(VOID) NtLmFree( Context->Password.Buffer );
|
|
}
|
|
if ( Context->DomainName.Buffer != NULL ) {
|
|
(VOID) NtLmFree( Context->DomainName.Buffer );
|
|
}
|
|
if ( Context->UserName.Buffer != NULL ) {
|
|
(VOID) NtLmFree( Context->UserName.Buffer );
|
|
}
|
|
if( Context->TargetInfo != NULL )
|
|
{
|
|
//
|
|
// CredUnmarshallTargetInfo uses LocalAlloc()
|
|
//
|
|
|
|
LocalFree( Context->TargetInfo );
|
|
}
|
|
|
|
if ( Context->TokenHandle != NULL ) {
|
|
NTSTATUS IgnoreStatus;
|
|
IgnoreStatus = NtClose( Context->TokenHandle );
|
|
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
|
}
|
|
|
|
if( Context->pbMarshalledTargetInfo )
|
|
{
|
|
LocalFree( Context->pbMarshalledTargetInfo );
|
|
}
|
|
|
|
if (Context->Credential != NULL) {
|
|
SspCredentialDereferenceCredential( Context->Credential );
|
|
}
|
|
|
|
ZeroMemory( Context, sizeof(SSP_CONTEXT) );
|
|
(VOID) NtLmFree( Context );
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PSSP_CONTEXT
|
|
SspContextAllocateContext(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates the security context block, initializes it and
|
|
links it onto the specified credential.
|
|
|
|
Arguments: None
|
|
|
|
Return Value:
|
|
|
|
NULL -- Not enough memory to allocate context.
|
|
|
|
otherwise -- pointer to allocated and referenced context.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
SspPrint(( SSP_API_MORE, "Entering SspContextAllocateContext\n" ));
|
|
PSSP_CONTEXT Context;
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
|
|
//
|
|
// Allocate a Context block and initialize it.
|
|
//
|
|
|
|
Context = (PSSP_CONTEXT)NtLmAllocate(sizeof(SSP_CONTEXT) );
|
|
|
|
if ( Context == NULL ) {
|
|
SspPrint(( SSP_CRITICAL, "SspContextAllocateContext: Error allocating Context.\n" ));
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory( Context, sizeof(SSP_CONTEXT) );
|
|
|
|
|
|
|
|
if( LsaFunctions->GetCallInfo(&CallInfo) ) {
|
|
Context->ClientProcessID = CallInfo.ProcessId;
|
|
Context->KernelClient = ((CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE) != 0);
|
|
}
|
|
|
|
//
|
|
// The reference count is set to 2. 1 to indicate it is on the
|
|
// valid Context list, and one for the our own reference.
|
|
//
|
|
|
|
Context->References = 2;
|
|
Context->State = IdleState;
|
|
|
|
//
|
|
// Timeout this context.
|
|
//
|
|
Context->TickStart = GetTickCount();
|
|
|
|
(VOID) NtQuerySystemTime( &Context->StartTime );
|
|
Context->Interval = NTLMSSP_MAX_LIFETIME;
|
|
|
|
//
|
|
// Add it to the list of valid Context handles.
|
|
//
|
|
|
|
Context->ContextTag = SSP_CONTEXT_TAG_ACTIVE;
|
|
|
|
RtlCopyMemory(
|
|
Context->ContextMagicNumber,
|
|
NtlmGlobalMagicNumber,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
|
|
SspPrint(( SSP_API_MORE, "Added Context 0x%lx\n", Context ));
|
|
|
|
SspPrint(( SSP_API_MORE, "Leaving SspContextAllocateContext\n" ));
|
|
|
|
return Context;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SspContextGetMessage(
|
|
IN PVOID InputMessage,
|
|
IN ULONG InputMessageSize,
|
|
IN NTLM_MESSAGE_TYPE ExpectedMessageType,
|
|
OUT PVOID* OutputMessage
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the InputMessage into the local address space.
|
|
This routine then validates the message header.
|
|
|
|
Arguments:
|
|
|
|
InputMessage - Address of the message in the client process.
|
|
|
|
InputMessageSize - Size of the message (in bytes).
|
|
|
|
ExpectedMessageType - The type of message the should be in the message
|
|
header.
|
|
|
|
OutputMessage - Returns a pointer to an allocated buffer that contains
|
|
the message. The buffer should be freed using NtLmFree.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Call completed successfully
|
|
|
|
SEC_E_INVALID_TOKEN -- Message improperly formatted
|
|
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory to allocate message
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PNEGOTIATE_MESSAGE TypicalMessage = NULL;
|
|
|
|
//
|
|
// Allocate a local buffer for the message.
|
|
//
|
|
|
|
ASSERT( NTLMSP_MAX_TOKEN_SIZE >= NTLMSSP_MAX_MESSAGE_SIZE );
|
|
if ( InputMessageSize > NTLMSSP_MAX_MESSAGE_SIZE ) {
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL, "SspContextGetMessage, invalid input message size.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
TypicalMessage = (PNEGOTIATE_MESSAGE)NtLmAllocate(InputMessageSize );
|
|
|
|
if ( TypicalMessage == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
SspPrint(( SSP_CRITICAL, "SspContextGetMessage: Error allocating TypicalMessage.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the message into the buffer
|
|
//
|
|
|
|
RtlCopyMemory( TypicalMessage,
|
|
InputMessage,
|
|
InputMessageSize );
|
|
|
|
//
|
|
// Validate the message header.
|
|
//
|
|
|
|
if ( strncmp( (const char *)TypicalMessage->Signature,
|
|
NTLMSSP_SIGNATURE,
|
|
sizeof(NTLMSSP_SIGNATURE)) != 0 ||
|
|
TypicalMessage->MessageType != ExpectedMessageType ) {
|
|
|
|
(VOID) NtLmFree( TypicalMessage );
|
|
TypicalMessage = NULL;
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL, "SspContextGetMessage, Bogus Message.\n" ));
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
*OutputMessage = TypicalMessage;
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
SspContextCopyString(
|
|
IN PVOID MessageBuffer,
|
|
OUT PSTRING32 OutString,
|
|
IN PSTRING InString,
|
|
IN OUT PCHAR *Where
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the InString into the MessageBuffer at Where.
|
|
It then updates OutString to be a descriptor for the copied string. The
|
|
descriptor 'address' is an offset from the MessageBuffer.
|
|
|
|
Where is updated to point to the next available space in the MessageBuffer.
|
|
|
|
The caller is responsible for any alignment requirements and for ensuring
|
|
there is room in the buffer for the string.
|
|
|
|
Arguments:
|
|
|
|
MessageBuffer - Specifies the base address of the buffer being copied into.
|
|
|
|
OutString - Returns a descriptor for the copied string. The descriptor
|
|
is relative to the begining of the buffer.
|
|
|
|
InString - Specifies the string to copy.
|
|
|
|
Where - On input, points to where the string is to be copied.
|
|
On output, points to the first byte after the string.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Copy the data to the Buffer.
|
|
//
|
|
|
|
if ( InString->Buffer != NULL ) {
|
|
RtlCopyMemory( *Where, InString->Buffer, InString->Length );
|
|
}
|
|
|
|
//
|
|
// Build a descriptor to the newly copied data.
|
|
//
|
|
|
|
OutString->Length = OutString->MaximumLength = InString->Length;
|
|
OutString->Buffer = (ULONG)(*Where - ((PCHAR)MessageBuffer));
|
|
|
|
|
|
//
|
|
// Update Where to point past the copied data.
|
|
//
|
|
|
|
*Where += InString->Length;
|
|
|
|
}
|
|
|
|
VOID
|
|
SspContextCopyStringAbsolute(
|
|
IN PVOID MessageBuffer,
|
|
OUT PSTRING OutString,
|
|
IN PSTRING InString,
|
|
IN OUT PCHAR *Where
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the InString into the MessageBuffer at Where.
|
|
It then updates OutString to be a descriptor for the copied string.
|
|
|
|
Where is updated to point to the next available space in the MessageBuffer.
|
|
|
|
The caller is responsible for any alignment requirements and for ensuring
|
|
there is room in the buffer for the string.
|
|
|
|
Arguments:
|
|
|
|
MessageBuffer - Specifies the base address of the buffer being copied into.
|
|
|
|
OutString - Returns a descriptor for the copied string. The descriptor
|
|
is relative to the begining of the buffer.
|
|
|
|
InString - Specifies the string to copy.
|
|
|
|
Where - On input, points to where the string is to be copied.
|
|
On output, points to the first byte after the string.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Copy the data to the Buffer.
|
|
//
|
|
|
|
if ( InString->Buffer != NULL ) {
|
|
RtlCopyMemory( *Where, InString->Buffer, InString->Length );
|
|
}
|
|
|
|
//
|
|
// Build a descriptor to the newly copied data.
|
|
//
|
|
|
|
OutString->Length = OutString->MaximumLength = InString->Length;
|
|
OutString->Buffer = *Where;
|
|
|
|
//
|
|
// Update Where to point past the copied data.
|
|
//
|
|
|
|
*Where += InString->Length;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SspConvertRelativeToAbsolute (
|
|
IN PVOID MessageBase,
|
|
IN ULONG MessageSize,
|
|
IN PSTRING32 StringToRelocate,
|
|
IN PSTRING OutputString,
|
|
IN BOOLEAN AlignToWchar,
|
|
IN BOOLEAN AllowNullString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a Relative string desriptor to be absolute.
|
|
Perform all boudary condition testing.
|
|
|
|
Arguments:
|
|
|
|
MessageBase - a pointer to the base of the buffer that the string
|
|
is relative to. The MaximumLength field of the descriptor is
|
|
forced to be the same as the Length field.
|
|
|
|
MessageSize - Size of the message buffer (in bytes).
|
|
|
|
StringToRelocate - A pointer to the string descriptor to make absolute.
|
|
|
|
AlignToWchar - If TRUE the passed in StringToRelocate must describe
|
|
a buffer that is WCHAR aligned. If not, an error is returned.
|
|
|
|
AllowNullString - If TRUE, the passed in StringToRelocate may be
|
|
a zero length string.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The string descriptor is valid and was properly relocated.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG_PTR Offset;
|
|
|
|
//
|
|
// If the buffer is allowed to be null,
|
|
// check that special case.
|
|
//
|
|
|
|
if ( AllowNullString ) {
|
|
if ( StringToRelocate->Length == 0 ) {
|
|
OutputString->MaximumLength = OutputString->Length = StringToRelocate->Length;
|
|
OutputString->Buffer = NULL;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ensure the string in entirely within the message.
|
|
//
|
|
|
|
Offset = (ULONG_PTR) StringToRelocate->Buffer;
|
|
|
|
if ( Offset >= MessageSize ||
|
|
Offset + StringToRelocate->Length > MessageSize ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure the buffer is properly aligned.
|
|
//
|
|
|
|
if ( AlignToWchar ) {
|
|
if ( !COUNT_IS_ALIGNED( Offset, ALIGN_WCHAR) ||
|
|
!COUNT_IS_ALIGNED( StringToRelocate->Length, ALIGN_WCHAR) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally make the pointer absolute.
|
|
//
|
|
|
|
OutputString->Buffer = (((PCHAR)MessageBase) + Offset);
|
|
OutputString->MaximumLength = OutputString->Length = StringToRelocate->Length ;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
TimeStamp
|
|
SspContextGetTimeStamp(
|
|
IN PSSP_CONTEXT Context,
|
|
IN BOOLEAN GetExpirationTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the Start time or Expiration time for the specified context.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to the context to query
|
|
|
|
GetExpirationTime - If TRUE return the expiration time.
|
|
Otherwise, return the start time for the context.
|
|
|
|
Return Value:
|
|
|
|
Returns the requested time as a local time.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER SystemTime;
|
|
LARGE_INTEGER LocalTime;
|
|
TimeStamp LocalTimeStamp;
|
|
|
|
//
|
|
// Get the requested time in NT system time format.
|
|
//
|
|
|
|
SystemTime = Context->StartTime;
|
|
|
|
if ( GetExpirationTime ) {
|
|
LARGE_INTEGER Interval;
|
|
|
|
//
|
|
// If the time is infinite, return that
|
|
//
|
|
|
|
if ( Context->Interval == INFINITE ) {
|
|
return NtLmGlobalForever;
|
|
}
|
|
|
|
//
|
|
// Compute the ending time in NT System Time.
|
|
//
|
|
|
|
Interval.QuadPart = Int32x32To64( (LONG) Context->Interval, 10000 );
|
|
SystemTime.QuadPart = Interval.QuadPart + SystemTime.QuadPart;
|
|
}
|
|
|
|
//
|
|
// Convert the time to local time
|
|
//
|
|
|
|
Status = RtlSystemTimeToLocalTime( &SystemTime, &LocalTime );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return NtLmGlobalForever;
|
|
}
|
|
|
|
LocalTimeStamp.HighPart = LocalTime.HighPart;
|
|
LocalTimeStamp.LowPart = LocalTime.LowPart;
|
|
|
|
return LocalTimeStamp;
|
|
|
|
}
|
|
|
|
VOID
|
|
SspContextSetTimeStamp(
|
|
IN PSSP_CONTEXT Context,
|
|
IN LARGE_INTEGER ExpirationTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the Expiration time for the specified context.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to the context to change
|
|
|
|
ExpirationTime - Expiration time to set
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LARGE_INTEGER BaseGetTickMagicDivisor = { 0xe219652c, 0xd1b71758 };
|
|
CCHAR BaseGetTickMagicShiftCount = 13;
|
|
|
|
LARGE_INTEGER TimeRemaining;
|
|
LARGE_INTEGER MillisecondsRemaining;
|
|
|
|
//
|
|
// If the expiration time is infinite,
|
|
// so is the interval
|
|
//
|
|
|
|
if ( ExpirationTime.HighPart == 0x7FFFFFFF &&
|
|
ExpirationTime.LowPart == 0xFFFFFFFF ) {
|
|
Context->Interval = INFINITE;
|
|
|
|
//
|
|
// Handle non-infinite expiration times
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Compute the time remaining before the expiration time
|
|
//
|
|
|
|
TimeRemaining.QuadPart = ExpirationTime.QuadPart -
|
|
Context->StartTime.QuadPart;
|
|
|
|
//
|
|
// If the time has already expired,
|
|
// indicate so.
|
|
//
|
|
|
|
if ( TimeRemaining.QuadPart < 0 ) {
|
|
|
|
Context->Interval = 0;
|
|
|
|
//
|
|
// If the time hasn't expired, compute the number of milliseconds
|
|
// remaining.
|
|
//
|
|
|
|
} else {
|
|
|
|
MillisecondsRemaining = RtlExtendedMagicDivide(
|
|
TimeRemaining,
|
|
BaseGetTickMagicDivisor,
|
|
BaseGetTickMagicShiftCount );
|
|
|
|
if ( MillisecondsRemaining.HighPart == 0 &&
|
|
MillisecondsRemaining.LowPart < 0x7fffffff ) {
|
|
|
|
Context->Interval = MillisecondsRemaining.LowPart;
|
|
|
|
} else {
|
|
|
|
Context->Interval = INFINITE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SsprDeleteSecurityContext (
|
|
IN OUT ULONG_PTR ContextHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the local data structures associated with the specified
|
|
security context and generates a token which is passed to a remote peer
|
|
so it too can remove the corresponding security context.
|
|
|
|
This API terminates a context on the local machine, and optionally
|
|
provides a token to be sent to the other machine. The OutputToken
|
|
generated by this call is to be sent to the remote peer (initiator or
|
|
acceptor). If the context was created with the I _REQ_ALLOCATE_MEMORY
|
|
flag, then the package will allocate a buffer for the output token.
|
|
Otherwise, it is the responsibility of the caller.
|
|
|
|
Arguments:
|
|
|
|
ContextHandle - Handle to the context to delete
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Call completed successfully
|
|
|
|
SEC_E_NO_SPM -- Security Support Provider is not running
|
|
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSSP_CONTEXT Context = NULL;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
SspPrint(( SSP_API_MORE, "SspDeleteSecurityContext Entered\n" ));
|
|
|
|
//
|
|
// Find the currently existing context (and delink it).
|
|
//
|
|
|
|
Status = SspContextReferenceContext( ContextHandle, TRUE, &Context );
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
SspContextDereferenceContext( Context );
|
|
}
|
|
|
|
SspPrint(( SSP_API_MORE, "SspDeleteSecurityContext returns 0x%lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SspContextInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes this module.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
SspContextTerminate(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function cleans up any dangling Contexts.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// don't forcibly try to delete contexts during shutdown, as, the
|
|
// state of the machine is in flux during shutdown.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
SsprCheckMinimumSecurity(
|
|
IN ULONG NegotiateFlags,
|
|
IN ULONG MinimumSecurityFlags
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Check that minimum security requirements have been met.
|
|
|
|
Arguments:
|
|
|
|
NegotiateFlags: requested security features
|
|
MinimumSecurityFlags: minimum required features
|
|
|
|
Return Value:
|
|
TRUE if minimum requirements met
|
|
FALSE otherwise
|
|
|
|
Notes:
|
|
The MinimumSecurityFlags can contain features that only apply if
|
|
a key is needed when doing signing or sealing. These have to be removed
|
|
if SIGN or SEAL is not in the NegotiateFlags.
|
|
|
|
--*/
|
|
{
|
|
ULONG EffFlags; // flags in effect
|
|
|
|
EffFlags = MinimumSecurityFlags;
|
|
|
|
|
|
if( (NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) == 0 )
|
|
EffFlags &= ~(NTLMSSP_NEGOTIATE_SIGN);
|
|
|
|
|
|
if( (NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) == 0 )
|
|
EffFlags &= ~(NTLMSSP_NEGOTIATE_SEAL);
|
|
|
|
|
|
//
|
|
// if SIGN or SEAL is not negotiated, then remove all key related
|
|
// requirements, since they're not relevant when a key isn't needed
|
|
//
|
|
|
|
if ((NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) == 0)
|
|
{
|
|
EffFlags &= ~(
|
|
NTLMSSP_NEGOTIATE_128 |
|
|
NTLMSSP_NEGOTIATE_56 |
|
|
NTLMSSP_NEGOTIATE_KEY_EXCH
|
|
);
|
|
} else if ((NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) == 0) {
|
|
|
|
//
|
|
// If SIGN is negotiated, but SEAL isn't, then remove flags
|
|
// that aren't relevant to encryption
|
|
//
|
|
|
|
EffFlags &= ~( NTLMSSP_NEGOTIATE_KEY_EXCH );
|
|
}
|
|
|
|
//
|
|
// FYI: flags that can be usefully spec'd even without SIGN or SEAL:
|
|
// NTLM2 -- forces stronger authentication
|
|
// All other flags should never be set.... and are nuked in initcomn
|
|
//
|
|
|
|
return ((NegotiateFlags & EffFlags) == EffFlags);
|
|
}
|
|
|
|
|
|
SECURITY_STATUS
|
|
SsprMakeSessionKey(
|
|
IN PSSP_CONTEXT Context,
|
|
IN PSTRING LmChallengeResponse,
|
|
IN UCHAR NtUserSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH], // from the DC or GetChalResp
|
|
IN UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH], // from the DC of GetChalResp
|
|
IN PSTRING DatagramSessionKey
|
|
)
|
|
|
|
/*++
|
|
// SsprMakeSessionKey
|
|
// on entry:
|
|
// if KEY_EXCH has been negotiated, then
|
|
// either Context->SessionKey has a random number to be encrypted
|
|
// to be sent to the server, or it has the encrypted session key
|
|
// received from the client
|
|
// if client, DatagramSessionKey must point to STRING set up to hold 16 byte key,
|
|
// but with 0 length.
|
|
// else Context->SessionKey and DatagramSessionKey are irrelevant on entry
|
|
// on exit:
|
|
// Context->SessionKey has the session key to be used for the rest of the session
|
|
// if (DatagramSessionKey != NULL) then if KEY_EXCH then it has the encrypted session key
|
|
// to send to the server, else it is zero length
|
|
//
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UCHAR LocalSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
|
|
|
// if we don't need to make any keys, just return
|
|
// RDR/SRV expect session key but don't ask for it! work-around this...
|
|
// if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN| NTLMSSP_NEGOTIATE_SEAL)) == 0)
|
|
// return(SEC_E_OK);
|
|
|
|
if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN| NTLMSSP_NEGOTIATE_SEAL)) == 0)
|
|
{
|
|
|
|
RtlCopyMemory(
|
|
Context->SessionKey,
|
|
NtUserSessionKey,
|
|
sizeof(LocalSessionKey)
|
|
);
|
|
|
|
return SEC_E_OK;
|
|
}
|
|
|
|
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
|
|
{
|
|
//
|
|
// when using NTLM2, LSA gets passed flags that cause
|
|
// it to make good session keys -- nothing for us to do
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
LocalSessionKey,
|
|
NtUserSessionKey,
|
|
sizeof(LocalSessionKey)
|
|
);
|
|
}
|
|
else if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY )
|
|
{
|
|
LM_OWF_PASSWORD LmKey;
|
|
LM_RESPONSE LmResponseKey;
|
|
|
|
BYTE TemporaryResponse[ LM_RESPONSE_LENGTH ];
|
|
|
|
RtlZeroMemory(
|
|
LocalSessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
|
|
if (LmChallengeResponse->Length > LM_RESPONSE_LENGTH)
|
|
return(SEC_E_UNSUPPORTED_FUNCTION);
|
|
|
|
ZeroMemory( TemporaryResponse, sizeof(TemporaryResponse) );
|
|
CopyMemory( TemporaryResponse, LmChallengeResponse->Buffer, LmChallengeResponse->Length );
|
|
|
|
//
|
|
// The LM session key is made by taking the LM sesion key
|
|
// given to us by the LSA, extending it to LM_OWF_LENGTH
|
|
// with our salt, and then producing a new challenge-response
|
|
// with it and the original challenge response. The key is
|
|
// made from the first 8 bytes of the key.
|
|
//
|
|
|
|
RtlCopyMemory( &LmKey,
|
|
LanmanSessionKey,
|
|
MSV1_0_LANMAN_SESSION_KEY_LENGTH );
|
|
|
|
memset( (PUCHAR)(&LmKey) + MSV1_0_LANMAN_SESSION_KEY_LENGTH,
|
|
NTLMSSP_KEY_SALT,
|
|
LM_OWF_PASSWORD_LENGTH - MSV1_0_LANMAN_SESSION_KEY_LENGTH );
|
|
|
|
Status = RtlCalculateLmResponse(
|
|
(PLM_CHALLENGE) TemporaryResponse,
|
|
&LmKey,
|
|
&LmResponseKey
|
|
);
|
|
|
|
ZeroMemory( TemporaryResponse, sizeof(TemporaryResponse) );
|
|
if (!NT_SUCCESS(Status))
|
|
return(SspNtStatusToSecStatus(Status, SEC_E_NO_CREDENTIALS));
|
|
|
|
RtlCopyMemory(
|
|
LocalSessionKey,
|
|
&LmResponseKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
} else {
|
|
|
|
RtlCopyMemory(
|
|
LocalSessionKey,
|
|
NtUserSessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// If we aren't doing key exchange, store the session key in the
|
|
// context. Otherwise encrypt the session key to send to the
|
|
// server.
|
|
//
|
|
|
|
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
|
|
|
struct RC4_KEYSTRUCT Rc4Key;
|
|
|
|
//
|
|
// make a key schedule from the temp key to form key exchange key
|
|
//
|
|
|
|
rc4_key(
|
|
&Rc4Key,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH,
|
|
LocalSessionKey
|
|
);
|
|
|
|
if (DatagramSessionKey == NULL)
|
|
{
|
|
//
|
|
// decrypt what's in Context->SessionKey, leave it there
|
|
//
|
|
|
|
rc4(
|
|
&Rc4Key,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH,
|
|
Context->SessionKey
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// set the proper length so client will send something (length was 0)
|
|
//
|
|
|
|
DatagramSessionKey->Length =
|
|
DatagramSessionKey->MaximumLength =
|
|
MSV1_0_USER_SESSION_KEY_LENGTH;
|
|
|
|
//
|
|
// copy randomly generated key to buffer to send to server
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
DatagramSessionKey->Buffer,
|
|
Context->SessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
|
|
//
|
|
// encrypt it with the key exchange key
|
|
//
|
|
|
|
rc4(
|
|
&Rc4Key,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH,
|
|
(unsigned char*)DatagramSessionKey->Buffer
|
|
);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// just make the temp key into the real one
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
Context->SessionKey,
|
|
LocalSessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
|
|
}
|
|
|
|
/*
|
|
//
|
|
// These casts currently generate an alignment fault on 64-bit and AV on 32-bit.
|
|
//
|
|
|
|
SspPrint((SSP_SESSION_KEYS, "SsprMakeSessionKey NegotiateFlags %lx, session key %lx %lx %lx %lx\n",
|
|
Context->NegotiateFlags,
|
|
((DWORD*)Context->SessionKey)[0],
|
|
((DWORD*)Context->SessionKey)[1],
|
|
((DWORD*)Context->SessionKey)[2],
|
|
((DWORD*)Context->SessionKey)[3]
|
|
));
|
|
|
|
SspPrint((SSP_SESSION_KEYS, "SsprMakeSessionKey LmChallengeResponse %lx %lx %lx %lx %lx %lx\n",
|
|
((DWORD*)LmChallengeResponse->Buffer)[0],
|
|
((DWORD*)LmChallengeResponse->Buffer)[1],
|
|
((DWORD*)LmChallengeResponse->Buffer)[2],
|
|
((DWORD*)LmChallengeResponse->Buffer)[3],
|
|
((DWORD*)LmChallengeResponse->Buffer)[4],
|
|
((DWORD*)LmChallengeResponse->Buffer)[5]
|
|
));
|
|
|
|
if (DatagramSessionKey && DatagramSessionKey->Length >= MSV1_0_USER_SESSION_KEY_LENGTH)
|
|
{
|
|
SspPrint((SSP_SESSION_KEYS, "SsprMakeSessionKey datagram session key %lx %lx %lx %lx\n",
|
|
((DWORD*)DatagramSessionKey->Buffer)[0],
|
|
((DWORD*)DatagramSessionKey->Buffer)[1],
|
|
((DWORD*)DatagramSessionKey->Buffer)[2],
|
|
((DWORD*)DatagramSessionKey->Buffer)[3]
|
|
));
|
|
|
|
}
|
|
*/
|
|
|
|
return(SEC_E_OK);
|
|
}
|
|
|
|
NTSTATUS
|
|
SsprQueryTreeName(
|
|
OUT PUNICODE_STRING TreeName
|
|
)
|
|
{
|
|
|
|
PDS_DOMAIN_TRUSTSW Trusts = NULL;
|
|
ULONG TrustCount ;
|
|
ULONG Index;
|
|
LPWSTR DnsTreeName = NULL;
|
|
|
|
DWORD NetStatus;
|
|
|
|
ZeroMemory( TreeName, sizeof(*TreeName) );
|
|
|
|
NetStatus = DsEnumerateDomainTrustsW(
|
|
NULL,
|
|
DS_DOMAIN_PRIMARY | DS_DOMAIN_IN_FOREST,
|
|
&Trusts,
|
|
&TrustCount
|
|
);
|
|
|
|
if( NetStatus != NO_ERROR )
|
|
{
|
|
// TODO: Talk to CliffV about failure causes.
|
|
// in any event, the failure is not catastrophic.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
for(Index = 0 ; Index < TrustCount; Index++)
|
|
{
|
|
ULONG Attempts; // bound the attempts in the event bogus data comes back.
|
|
|
|
if( (Trusts[Index].Flags & DS_DOMAIN_PRIMARY) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( Attempts = 0 ; Index < TrustCount; Attempts++ )
|
|
{
|
|
if( Attempts > TrustCount )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( (Trusts[Index].Flags & DS_DOMAIN_TREE_ROOT) == 0 )
|
|
{
|
|
Index = Trusts[Index].ParentIndex;
|
|
continue;
|
|
}
|
|
|
|
DnsTreeName = Trusts[Index].DnsDomainName;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if( DnsTreeName )
|
|
{
|
|
DWORD cchTreeName = lstrlenW( DnsTreeName );
|
|
|
|
TreeName->Buffer = (PWSTR)NtLmAllocate( cchTreeName*sizeof(WCHAR) );
|
|
if( TreeName->Buffer == NULL )
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
TreeName->Length = (USHORT)(cchTreeName*sizeof(WCHAR));
|
|
TreeName->MaximumLength = TreeName->Length;
|
|
|
|
RtlCopyMemory( TreeName->Buffer, DnsTreeName, TreeName->Length );
|
|
}
|
|
|
|
NetApiBufferFree( Trusts );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SsprUpdateTargetInfo(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Update the NtLmGlobalNtLm3TargetInfo buffer based on the current values
|
|
of the various global variables.
|
|
|
|
NOTE: the global lock must be held for exclusive access prior to making
|
|
this call!
|
|
|
|
--*/
|
|
{
|
|
PMSV1_0_AV_PAIR pAV;
|
|
PUNICODE_STRING pDnsTargetName;
|
|
PUNICODE_STRING pDnsComputerName;
|
|
PUNICODE_STRING pDnsTreeName;
|
|
ULONG cbAV;
|
|
|
|
ULONG AvFlags = 0;
|
|
|
|
if( NtLmGlobalNtLm3TargetInfo.Buffer != NULL )
|
|
{
|
|
NtLmFree(NtLmGlobalNtLm3TargetInfo.Buffer);
|
|
NtLmGlobalNtLm3TargetInfo.Buffer = NULL;
|
|
NtLmGlobalNtLm3TargetInfo.MaximumLength = NtLmGlobalNtLm3TargetInfo.Length = 0;
|
|
}
|
|
|
|
if( NtLmGlobalTargetFlags == NTLMSSP_TARGET_TYPE_DOMAIN ) {
|
|
pDnsTargetName = &NtLmGlobalUnicodeDnsDomainNameString;
|
|
} else {
|
|
pDnsTargetName = &NtLmGlobalUnicodeDnsComputerNameString;
|
|
}
|
|
|
|
pDnsComputerName = &NtLmGlobalUnicodeDnsComputerNameString;
|
|
|
|
pDnsTreeName = &NtLmGlobalUnicodeDnsTreeName;
|
|
|
|
cbAV = NtLmGlobalUnicodeTargetName.Length +
|
|
NtLmGlobalUnicodeComputerNameString.Length +
|
|
pDnsComputerName->Length +
|
|
pDnsTargetName->Length +
|
|
pDnsTreeName->Length +
|
|
sizeof( AvFlags ) +
|
|
(sizeof( MSV1_0_AV_PAIR ) * 6) +
|
|
sizeof( MSV1_0_AV_PAIR );
|
|
|
|
NtLmGlobalNtLm3TargetInfo.Buffer = (PWSTR)NtLmAllocate( cbAV );
|
|
|
|
if( NtLmGlobalNtLm3TargetInfo.Buffer == NULL )
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SsprUpdateTargetInfo, Error from NtLmAllocate\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ZeroMemory( NtLmGlobalNtLm3TargetInfo.Buffer, cbAV );
|
|
|
|
pAV = MsvpAvlInit( NtLmGlobalNtLm3TargetInfo.Buffer );
|
|
MsvpAvlAdd( pAV, MsvAvNbDomainName, &NtLmGlobalUnicodeTargetName, cbAV );
|
|
MsvpAvlAdd( pAV, MsvAvNbComputerName, &NtLmGlobalUnicodeComputerNameString, cbAV );
|
|
|
|
if( pDnsTargetName->Length != 0 && pDnsTargetName->Buffer != NULL )
|
|
{
|
|
MsvpAvlAdd( pAV, MsvAvDnsDomainName, pDnsTargetName, cbAV );
|
|
}
|
|
|
|
if( pDnsComputerName->Length != 0 && pDnsComputerName->Buffer != NULL )
|
|
{
|
|
MsvpAvlAdd( pAV, MsvAvDnsComputerName, pDnsComputerName, cbAV );
|
|
}
|
|
|
|
if( pDnsTreeName->Length != 0 && pDnsTreeName->Buffer != NULL )
|
|
{
|
|
MsvpAvlAdd( pAV, MsvAvDnsTreeName, pDnsTreeName, cbAV );
|
|
}
|
|
|
|
//
|
|
// add in AvFlags into TargetInfo, if applicable.
|
|
//
|
|
|
|
if( NtLmGlobalForceGuest )
|
|
{
|
|
AvFlags |= MSV1_0_AV_FLAG_FORCE_GUEST;
|
|
}
|
|
|
|
if( AvFlags )
|
|
{
|
|
UNICODE_STRING AvString;
|
|
|
|
AvString.Buffer = (PWSTR)&AvFlags;
|
|
AvString.Length = sizeof( AvFlags );
|
|
AvString.MaximumLength = AvString.Length;
|
|
|
|
MsvpAvlAdd( pAV, MsvAvFlags, &AvString, cbAV );
|
|
}
|
|
|
|
|
|
NtLmGlobalNtLm3TargetInfo.Length = (USHORT)MsvpAvlLen( pAV, cbAV );
|
|
NtLmGlobalNtLm3TargetInfo.MaximumLength = NtLmGlobalNtLm3TargetInfo.Length;
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|