Leaked source code of windows server 2003
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

/*++
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;
}