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.
2389 lines
79 KiB
2389 lines
79 KiB
/*++
|
|
|
|
Copyright (c) 1993-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ctxtsrv.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
|
|
JClark 28-Jun-2000 Added WMI Trace Logging Support
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include <global.h>
|
|
#include <align.h> // ALIGN_WCHAR, etc
|
|
#include "Trace.h"
|
|
|
|
|
|
NTSTATUS
|
|
SsprHandleNegotiateMessage(
|
|
IN ULONG_PTR CredentialHandle,
|
|
IN OUT PULONG_PTR ContextHandle,
|
|
IN ULONG ContextReqFlags,
|
|
IN ULONG InputTokenSize,
|
|
IN PVOID InputToken,
|
|
IN OUT PULONG OutputTokenSize,
|
|
OUT PVOID *OutputToken,
|
|
OUT PULONG ContextAttributes,
|
|
OUT PTimeStamp ExpirationTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the Negotiate message part of AcceptSecurityContext.
|
|
|
|
Arguments:
|
|
|
|
All arguments same as for AcceptSecurityContext
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Message handled
|
|
SEC_I_CONTINUE_NEEDED -- Caller should call again later
|
|
|
|
SEC_E_INVALID_TOKEN -- Token improperly formatted
|
|
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
|
|
SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
|
|
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSSP_CONTEXT Context = NULL;
|
|
PSSP_CREDENTIAL Credential = NULL;
|
|
STRING TargetName;
|
|
ULONG TargetFlags = 0;
|
|
|
|
PNEGOTIATE_MESSAGE NegotiateMessage = NULL;
|
|
|
|
PCHALLENGE_MESSAGE ChallengeMessage = NULL;
|
|
ULONG ChallengeMessageSize = 0;
|
|
PCHAR Where = NULL;
|
|
|
|
ULONG NegotiateFlagsKeyStrength;
|
|
|
|
UNICODE_STRING NtLmLocalUnicodeTargetName;
|
|
UNICODE_STRING TargetInfo;
|
|
STRING NtLmLocalOemTargetName;
|
|
STRING OemWorkstationName;
|
|
STRING OemDomainName;
|
|
|
|
SspPrint(( SSP_API_MORE, "Entering SsprNegotiateMessage\n" ));
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
*ContextAttributes = 0;
|
|
|
|
RtlInitString( &TargetName, NULL );
|
|
|
|
RtlInitUnicodeString( &NtLmLocalUnicodeTargetName, NULL );
|
|
RtlInitString( &NtLmLocalOemTargetName, NULL );
|
|
|
|
RtlInitUnicodeString( &TargetInfo, NULL );
|
|
|
|
//
|
|
// Get a pointer to the credential
|
|
//
|
|
|
|
Status = SspCredentialReferenceCredential(
|
|
CredentialHandle,
|
|
FALSE,
|
|
&Credential );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: invalid credential handle.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( (Credential->CredentialUseFlags & SECPKG_CRED_INBOUND) == 0 ) {
|
|
Status = SEC_E_INVALID_CREDENTIAL_USE;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: invalid credential use.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a new context
|
|
//
|
|
|
|
Context = SspContextAllocateContext( );
|
|
|
|
if ( Context == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: SspContextAllocateContext() returned NULL.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Build a handle to the newly created context.
|
|
//
|
|
|
|
*ContextHandle = (ULONG_PTR) Context;
|
|
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_IDENTIFY) != 0 ) {
|
|
|
|
*ContextAttributes |= ASC_RET_IDENTIFY;
|
|
Context->ContextFlags |= ASC_RET_IDENTIFY;
|
|
}
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_DATAGRAM) != 0 ) {
|
|
|
|
*ContextAttributes |= ASC_RET_DATAGRAM;
|
|
Context->ContextFlags |= ASC_RET_DATAGRAM;
|
|
}
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_CONNECTION) != 0 ) {
|
|
|
|
*ContextAttributes |= ASC_RET_CONNECTION;
|
|
Context->ContextFlags |= ASC_RET_CONNECTION;
|
|
}
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_INTEGRITY) != 0 ) {
|
|
|
|
*ContextAttributes |= ASC_RET_INTEGRITY;
|
|
Context->ContextFlags |= ASC_RET_INTEGRITY;
|
|
}
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_REPLAY_DETECT) != 0){
|
|
|
|
*ContextAttributes |= ASC_RET_REPLAY_DETECT;
|
|
Context->ContextFlags |= ASC_RET_REPLAY_DETECT;
|
|
}
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_SEQUENCE_DETECT ) != 0) {
|
|
|
|
*ContextAttributes |= ASC_RET_SEQUENCE_DETECT;
|
|
Context->ContextFlags |= ASC_RET_SEQUENCE_DETECT;
|
|
}
|
|
|
|
// Nothing to return, we might need this on the next server side call.
|
|
if ( (ContextReqFlags & ASC_REQ_ALLOW_NULL_SESSION ) != 0) {
|
|
|
|
Context->ContextFlags |= ASC_REQ_ALLOW_NULL_SESSION;
|
|
}
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_ALLOW_NON_USER_LOGONS ) != 0) {
|
|
|
|
*ContextAttributes |= ASC_RET_ALLOW_NON_USER_LOGONS;
|
|
Context->ContextFlags |= ASC_RET_ALLOW_NON_USER_LOGONS;
|
|
}
|
|
|
|
if ( ContextReqFlags & ASC_REQ_CONFIDENTIALITY ) {
|
|
|
|
if (NtLmGlobalEncryptionEnabled) {
|
|
*ContextAttributes |= ASC_RET_CONFIDENTIALITY;
|
|
Context->ContextFlags |= ASC_RET_CONFIDENTIALITY;
|
|
} else {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: invalid ContextReqFlags 0x%lx\n", ContextReqFlags ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Supported key strength(s)
|
|
//
|
|
|
|
NegotiateFlagsKeyStrength = NTLMSSP_NEGOTIATE_56;
|
|
NegotiateFlagsKeyStrength |= NTLMSSP_NEGOTIATE_128;
|
|
|
|
//
|
|
// Get the NegotiateMessage. If we are re-establishing a datagram
|
|
// context then there may not be one.
|
|
//
|
|
|
|
if ( InputTokenSize >= sizeof(OLD_NEGOTIATE_MESSAGE) ) {
|
|
|
|
Status = SspContextGetMessage( InputToken,
|
|
InputTokenSize,
|
|
NtLmNegotiate,
|
|
(PVOID *)&NegotiateMessage );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: "
|
|
"NegotiateMessage GetMessage returns 0x%lx\n",
|
|
Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Compute the TargetName to return in the ChallengeMessage.
|
|
//
|
|
|
|
if ( NegotiateMessage->NegotiateFlags & NTLMSSP_REQUEST_TARGET ||
|
|
NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2 ) {
|
|
|
|
RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
|
|
if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE) {
|
|
Status = NtLmDuplicateUnicodeString( &NtLmLocalUnicodeTargetName, &NtLmGlobalUnicodeTargetName );
|
|
TargetName = *((PSTRING)&NtLmLocalUnicodeTargetName);
|
|
} else {
|
|
Status = NtLmDuplicateString( &NtLmLocalOemTargetName, &NtLmGlobalOemTargetName );
|
|
TargetName = NtLmLocalOemTargetName;
|
|
}
|
|
|
|
//
|
|
// if client is NTLM2-aware, send it target info AV pairs
|
|
//
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = NtLmDuplicateUnicodeString( &TargetInfo, &NtLmGlobalNtLm3TargetInfo );
|
|
}
|
|
|
|
TargetFlags = NtLmGlobalTargetFlags;
|
|
RtlReleaseResource (&NtLmGlobalCritSect);
|
|
|
|
TargetFlags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: "
|
|
"failed to duplicate UnicodeTargetName or OemTargetName error 0x%lx\n",
|
|
Status ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else {
|
|
TargetFlags = 0;
|
|
}
|
|
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) {
|
|
if (InputTokenSize >= RTL_SIZEOF_THROUGH_FIELD(NEGOTIATE_MESSAGE, Version)) {
|
|
C_ASSERT(sizeof(NTLM_VER_INFO) == sizeof(ULONG64));
|
|
|
|
RtlCopyMemory(&Context->ClientVersion, &NegotiateMessage->Version, sizeof(NegotiateMessage->Version));
|
|
|
|
SspPrint(( SSP_VERSION,
|
|
"SsprHandleNegotiateMessage: ClientVersion %#I64x, Major %I64d, Minor %I64d, Build %I64d, Revision %I64d\n",
|
|
NegotiateMessage->Version,
|
|
Context->ClientVersion.Major,
|
|
Context->ClientVersion.Minor,
|
|
Context->ClientVersion.Build,
|
|
Context->ClientVersion.Revision ));
|
|
|
|
} else {
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: NegotiateMessage size too small with NTLMSSP_NEGOTIATE_VERSION\n" ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate a Challenge message
|
|
//
|
|
|
|
ChallengeMessageSize = sizeof(*ChallengeMessage) +
|
|
TargetName.Length +
|
|
TargetInfo.Length;
|
|
|
|
if ((ContextReqFlags & ASC_REQ_ALLOCATE_MEMORY) == 0)
|
|
{
|
|
if ( ChallengeMessageSize > *OutputTokenSize ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: invalid ChallengeMessageSize\n"));
|
|
Status = SEC_E_BUFFER_TOO_SMALL;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
ChallengeMessage = (PCHALLENGE_MESSAGE)
|
|
NtLmAllocateLsaHeap( ChallengeMessageSize );
|
|
|
|
if ( ChallengeMessage == NULL ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: Error allocating ChallengeMessage.\n" ));
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ChallengeMessage->NegotiateFlags = NTLMSSP_NEGOTIATE_VERSION;
|
|
|
|
//
|
|
// Check that both sides can use the same authentication model. For
|
|
// compatibility with beta 1 and 2 (builds 612 and 683), no requested
|
|
// authentication type is assumed to be NTLM. If NetWare is explicitly
|
|
// asked for, it is assumed that NTLM would have been also, so if it
|
|
// wasn't, return an error.
|
|
//
|
|
|
|
if ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NETWARE) &&
|
|
((NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM) == 0) &&
|
|
((NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) == 0)
|
|
) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: "
|
|
"NegotiateMessage asked for Netware only.\n" ));
|
|
goto Cleanup;
|
|
} else {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
|
|
}
|
|
|
|
//
|
|
// if client can do NTLM2, nuke LM_KEY
|
|
//
|
|
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) {
|
|
NegotiateMessage->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
|
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM2;
|
|
} else if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
|
|
}
|
|
|
|
//
|
|
// If the client wants to always sign messages, so be it.
|
|
//
|
|
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN ) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
|
}
|
|
|
|
//
|
|
// If the caller wants identify level, so be it.
|
|
//
|
|
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY ) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_IDENTIFY;
|
|
|
|
*ContextAttributes |= ASC_RET_IDENTIFY;
|
|
Context->ContextFlags |= ASC_RET_IDENTIFY;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine if the caller wants OEM or UNICODE
|
|
//
|
|
// Prefer UNICODE if caller allows both.
|
|
//
|
|
|
|
if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
|
|
} else if ( NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_OEM ){
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
|
|
} else {
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: "
|
|
"NegotiateMessage bad NegotiateFlags 0x%lx\n",
|
|
NegotiateMessage->NegotiateFlags ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Client wants Sign capability, OK.
|
|
//
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
|
|
|
|
*ContextAttributes |= (ASC_RET_SEQUENCE_DETECT | ASC_RET_REPLAY_DETECT);
|
|
Context->ContextFlags |= (ASC_RET_SEQUENCE_DETECT | ASC_RET_REPLAY_DETECT);
|
|
|
|
}
|
|
|
|
//
|
|
// Client wants Seal, OK.
|
|
//
|
|
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL)
|
|
{
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
|
|
|
|
*ContextAttributes |= ASC_RET_CONFIDENTIALITY;
|
|
Context->ContextFlags |= ASC_RET_CONFIDENTIALITY;
|
|
}
|
|
|
|
if (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
|
{
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
|
|
|
}
|
|
|
|
if ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_56) &&
|
|
(NegotiateFlagsKeyStrength & NTLMSSP_NEGOTIATE_56) )
|
|
{
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
|
|
}
|
|
|
|
if ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_128) &&
|
|
(NegotiateFlagsKeyStrength & NTLMSSP_NEGOTIATE_128) )
|
|
{
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
|
|
}
|
|
|
|
|
|
//
|
|
// If the client supplied the Domain Name and User Name,
|
|
// and did not request datagram, see if the client is running
|
|
// on this local machine.
|
|
//
|
|
|
|
if ( ( (NegotiateMessage->NegotiateFlags &
|
|
NTLMSSP_NEGOTIATE_DATAGRAM) == 0) &&
|
|
( (NegotiateMessage->NegotiateFlags &
|
|
(NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED|
|
|
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED)) ==
|
|
(NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED|
|
|
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) ) ) {
|
|
|
|
//
|
|
// The client must pass the new negotiate message if they pass
|
|
// these flags
|
|
//
|
|
|
|
if (InputTokenSize < RTL_SIZEOF_THROUGH_FIELD(NEGOTIATE_MESSAGE, OemWorkstationName)) {
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: invalid InputTokenSize.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Convert the names to absolute references so we
|
|
// can compare them
|
|
//
|
|
|
|
if ( !SspConvertRelativeToAbsolute(
|
|
NegotiateMessage,
|
|
InputTokenSize,
|
|
&NegotiateMessage->OemDomainName,
|
|
&OemDomainName,
|
|
FALSE, // No special alignment
|
|
FALSE ) ) { // NULL not OK
|
|
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: Error from SspConvertRelativeToAbsolute.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !SspConvertRelativeToAbsolute(
|
|
NegotiateMessage,
|
|
InputTokenSize,
|
|
&NegotiateMessage->OemWorkstationName,
|
|
&OemWorkstationName,
|
|
FALSE, // No special alignment
|
|
FALSE ) ) { // NULL not OK
|
|
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: Error from SspConvertRelativeToAbsolute.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If both strings match,
|
|
// this is a local call.
|
|
// The strings have already been uppercased.
|
|
//
|
|
|
|
RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
|
|
|
|
if ( RtlEqualString( &OemWorkstationName,
|
|
&NtLmGlobalOemComputerNameString,
|
|
FALSE ) &&
|
|
RtlEqualString( &OemDomainName,
|
|
&NtLmGlobalOemPrimaryDomainNameString,
|
|
FALSE )
|
|
)
|
|
{
|
|
#if DBG
|
|
IF_DEBUG( NO_LOCAL ) {
|
|
// nothing.
|
|
} else {
|
|
#endif
|
|
ChallengeMessage->NegotiateFlags |=
|
|
NTLMSSP_NEGOTIATE_LOCAL_CALL;
|
|
SspPrint(( SSP_MISC,
|
|
"SsprHandleNegotiateMessage: Local Call.\n" ));
|
|
|
|
ChallengeMessage->ServerContextHandle = (ULONG64)*ContextHandle;
|
|
#if DBG
|
|
}
|
|
#endif
|
|
}
|
|
RtlReleaseResource (&NtLmGlobalCritSect);
|
|
}
|
|
|
|
//
|
|
// Check if datagram is being negotiated
|
|
//
|
|
|
|
if ( (NegotiateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) ==
|
|
NTLMSSP_NEGOTIATE_DATAGRAM) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_DATAGRAM;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// No negotiate message. We need to check if the caller is asking
|
|
// for datagram.
|
|
//
|
|
|
|
if ((ContextReqFlags & ASC_REQ_DATAGRAM) == 0 ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: "
|
|
"NegotiateMessage size wrong %ld\n",
|
|
InputTokenSize ));
|
|
Status = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// always send target info -- new for NTLM3!
|
|
//
|
|
|
|
TargetFlags = NTLMSSP_NEGOTIATE_TARGET_INFO;
|
|
|
|
RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
|
|
|
|
Status = NtLmDuplicateUnicodeString( &TargetInfo, &NtLmGlobalNtLm3TargetInfo );
|
|
|
|
RtlReleaseResource(&NtLmGlobalCritSect);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage(datagram): Error duplicate TargetInfo %#x\n", Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a Challenge message
|
|
//
|
|
|
|
ChallengeMessageSize = sizeof(*ChallengeMessage) + TargetInfo.Length;
|
|
|
|
if ((ContextReqFlags & ASC_REQ_ALLOCATE_MEMORY) == 0)
|
|
{
|
|
if ( ChallengeMessageSize > *OutputTokenSize ) {
|
|
Status = SEC_E_BUFFER_TOO_SMALL;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: invalid ChallengeMessageSize.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
ChallengeMessage = (PCHALLENGE_MESSAGE)
|
|
NtLmAllocateLsaHeap(ChallengeMessageSize );
|
|
|
|
if ( ChallengeMessage == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: Error allocating ChallengeMessage.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Record in the context that we are doing datagram. We will tell
|
|
// the client everything we can negotiate and let it decide what
|
|
// to negotiate.
|
|
//
|
|
|
|
ChallengeMessage->NegotiateFlags = NTLMSSP_NEGOTIATE_DATAGRAM |
|
|
NTLMSSP_NEGOTIATE_UNICODE |
|
|
NTLMSSP_NEGOTIATE_OEM |
|
|
NTLMSSP_NEGOTIATE_SIGN |
|
|
NTLMSSP_NEGOTIATE_LM_KEY |
|
|
NTLMSSP_NEGOTIATE_NTLM |
|
|
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
|
NTLMSSP_NEGOTIATE_IDENTIFY |
|
|
NTLMSSP_NEGOTIATE_NTLM2 |
|
|
NTLMSSP_NEGOTIATE_KEY_EXCH |
|
|
NegotiateFlagsKeyStrength |
|
|
NTLMSSP_NEGOTIATE_VERSION;
|
|
|
|
if (NtLmGlobalEncryptionEnabled) {
|
|
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the Challenge Message
|
|
//
|
|
|
|
strcpy( (char *) ChallengeMessage->Signature, NTLMSSP_SIGNATURE );
|
|
ChallengeMessage->MessageType = NtLmChallenge;
|
|
ChallengeMessage->Version = NTLMSSP_ENGINE_VERSION;
|
|
Status = SspGenerateRandomBits( (UCHAR*)ChallengeMessage->Challenge,
|
|
MSV1_0_CHALLENGE_LENGTH );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: SspGenerateRandomBits failed\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Where = (PCHAR)(ChallengeMessage + 1);
|
|
|
|
SspContextCopyString( ChallengeMessage,
|
|
&ChallengeMessage->TargetName,
|
|
&TargetName,
|
|
&Where );
|
|
|
|
SspContextCopyString( ChallengeMessage,
|
|
&ChallengeMessage->TargetInfo,
|
|
(PSTRING)&TargetInfo,
|
|
&Where );
|
|
|
|
ChallengeMessage->NegotiateFlags |= TargetFlags;
|
|
|
|
//
|
|
// Save the Challenge and Negotiate Flags in the Context so it
|
|
// is available when the authenticate message comes in.
|
|
//
|
|
|
|
RtlCopyMemory( Context->Challenge,
|
|
ChallengeMessage->Challenge,
|
|
sizeof( Context->Challenge ) );
|
|
|
|
Context->NegotiateFlags = ChallengeMessage->NegotiateFlags;
|
|
|
|
if (!SsprCheckMinimumSecurity(
|
|
Context->NegotiateFlags,
|
|
NtLmGlobalMinimumServerSecurity)) {
|
|
|
|
Status = SEC_E_UNSUPPORTED_FUNCTION;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleNegotiateMessage: "
|
|
"NegotiateMessage didn't support minimum security requirements. (caller=0x%lx wanted=0x%lx\n",
|
|
Context->NegotiateFlags, NtLmGlobalMinimumServerSecurity ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
if ((ContextReqFlags & ASC_REQ_ALLOCATE_MEMORY) == 0)
|
|
{
|
|
RtlCopyMemory( *OutputToken,
|
|
ChallengeMessage,
|
|
ChallengeMessageSize );
|
|
}
|
|
else
|
|
{
|
|
*OutputToken = ChallengeMessage;
|
|
ChallengeMessage = NULL;
|
|
*ContextAttributes |= ASC_RET_ALLOCATED_MEMORY;
|
|
}
|
|
|
|
*OutputTokenSize = ChallengeMessageSize;
|
|
|
|
//
|
|
// Return output parameters to the caller.
|
|
//
|
|
|
|
*ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
|
|
Context->State = ChallengeSentState;
|
|
|
|
Status = SEC_I_CONTINUE_NEEDED;
|
|
|
|
//
|
|
// Free and locally used resources.
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
if ( Context != NULL ) {
|
|
|
|
//
|
|
// If we failed, deallocate the context we allocated above.
|
|
// Delinking is a side effect of referencing, so do that.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
PSSP_CONTEXT LocalContext;
|
|
|
|
SspContextReferenceContext( *ContextHandle,
|
|
TRUE,
|
|
&LocalContext
|
|
);
|
|
|
|
ASSERT( LocalContext != NULL );
|
|
if ( LocalContext != NULL ) {
|
|
SspContextDereferenceContext( LocalContext );
|
|
}
|
|
}
|
|
|
|
// Always dereference it.
|
|
|
|
SspContextDereferenceContext( Context );
|
|
}
|
|
|
|
if ( NegotiateMessage != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( NegotiateMessage );
|
|
}
|
|
|
|
if ( ChallengeMessage != NULL ) {
|
|
(VOID) NtLmFreeLsaHeap( ChallengeMessage );
|
|
}
|
|
|
|
if ( Credential != NULL ) {
|
|
SspCredentialDereferenceCredential( Credential );
|
|
}
|
|
|
|
if ( NtLmLocalUnicodeTargetName.Buffer != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( NtLmLocalUnicodeTargetName.Buffer );
|
|
}
|
|
|
|
if ( NtLmLocalOemTargetName.Buffer != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( NtLmLocalOemTargetName.Buffer );
|
|
}
|
|
|
|
if (TargetInfo.Buffer != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( TargetInfo.Buffer );
|
|
}
|
|
|
|
SspPrint(( SSP_API_MORE, "Leaving SsprHandleNegotiateMessage: 0x%lx\n", Status ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SsprHandleAuthenticateMessage(
|
|
IN LSA_SEC_HANDLE CredentialHandle,
|
|
IN OUT PLSA_SEC_HANDLE ContextHandle,
|
|
IN ULONG ContextReqFlags,
|
|
IN ULONG InputTokenSize,
|
|
IN PVOID InputToken,
|
|
IN ULONG SecondInputTokenSize,
|
|
IN PVOID SecondInputToken,
|
|
IN OUT PULONG OutputTokenSize,
|
|
OUT PVOID *OutputToken,
|
|
OUT PULONG ContextAttributes,
|
|
OUT PTimeStamp ExpirationTime,
|
|
OUT PUCHAR SessionKey,
|
|
OUT PULONG NegotiateFlags,
|
|
OUT PHANDLE TokenHandle,
|
|
OUT PNTSTATUS ApiSubStatus,
|
|
OUT PTimeStamp PasswordExpiry,
|
|
OUT PULONG UserFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the authenticate message part of AcceptSecurityContext.
|
|
|
|
Arguments:
|
|
|
|
SessionKey - The session key for the context, used for signing and sealing
|
|
|
|
NegotiateFlags - The flags negotiated for the context, used for sign & seal
|
|
|
|
ApiSubStatus - Returns the substatus for why the logon failed.
|
|
|
|
PasswordExpiry - Contains the time that the authenticated user's password
|
|
expires, or 0x7fffffff ffffffff for local callers.
|
|
|
|
UserFlags - UserFlags returned in LogonProfile.
|
|
|
|
All other arguments same as for AcceptSecurityContext
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Message handled
|
|
|
|
SEC_E_INVALID_TOKEN -- Token improperly formatted
|
|
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
|
|
SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
|
|
SEC_E_LOGON_DENIED -- User is no allowed to logon to this server
|
|
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
|
|
|
|
--*/
|
|
|
|
{
|
|
SECURITY_STATUS SecStatus = STATUS_SUCCESS;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSSP_CONTEXT Context = NULL;
|
|
|
|
PNEGOTIATE_MESSAGE NegotiateMessage = NULL;
|
|
PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL;
|
|
PNTLM_AUTHENTICATE_MESSAGE NtLmAuthenticateMessage = NULL;
|
|
PNTLM_ACCEPT_RESPONSE NtLmAcceptResponse = NULL;
|
|
ULONG MsvLogonMessageSize = 0;
|
|
PMSV1_0_LM20_LOGON MsvLogonMessage = NULL;
|
|
ULONG MsvSubAuthLogonMessageSize = 0;
|
|
PMSV1_0_SUBAUTH_LOGON MsvSubAuthLogonMessage = NULL;
|
|
ULONG LogonProfileMessageSize;
|
|
PMSV1_0_LM20_LOGON_PROFILE LogonProfileMessage = NULL;
|
|
LSA_TOKEN_INFORMATION_TYPE TokenInformationType = LsaTokenInformationNull;
|
|
|
|
BOOLEAN DoUnicode = FALSE;
|
|
STRING DomainName2;
|
|
STRING UserName2;
|
|
STRING Workstation2;
|
|
STRING SessionKeyString = {0, 0, NULL};
|
|
UNICODE_STRING DomainName;
|
|
UNICODE_STRING UserName;
|
|
UNICODE_STRING Workstation;
|
|
LARGE_INTEGER KickOffTime;
|
|
|
|
LUID LogonId = {0};
|
|
HANDLE LocalTokenHandle = NULL;
|
|
BOOLEAN LocalTokenHandleOpenned = FALSE;
|
|
TOKEN_SOURCE SourceContext;
|
|
NTSTATUS SubStatus = STATUS_SUCCESS;
|
|
STRING OriginName;
|
|
PCHAR Where;
|
|
PSSP_CREDENTIAL Credential = NULL;
|
|
BOOLEAN fCallFromSrv = FALSE;
|
|
PUNICODE_STRING AccountName = NULL;
|
|
PUNICODE_STRING AuthenticatingAuthority = NULL;
|
|
PUNICODE_STRING WorkstationName = NULL;
|
|
STRING NtChallengeResponse;
|
|
STRING LmChallengeResponse;
|
|
BOOL fSubAuth = FALSE;
|
|
ULONG SubAuthPackageId = 0;
|
|
PSID AllocatedAuditSid = NULL ;
|
|
PSID AuditSid = NULL;
|
|
BOOLEAN fAvoidGuestAudit = FALSE;
|
|
SECPKG_PRIMARY_CRED PrimaryCredentials;
|
|
BOOL IsDatagramLmKeyCorrectionOn = FALSE;
|
|
|
|
//Tracing State
|
|
NTLM_TRACE_INFO TraceInfo = {0};
|
|
PLSA_SEC_HANDLE TraceOldContextHandle = ContextHandle;
|
|
|
|
ASSERT(LM_RESPONSE_LENGTH >= MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
|
|
SspPrint(( SSP_API_MORE, "Entering SsprHandleAuthenticateMessage\n"));
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
*ContextAttributes = 0;
|
|
RtlInitUnicodeString(
|
|
&DomainName,
|
|
NULL
|
|
);
|
|
RtlInitUnicodeString(
|
|
&UserName,
|
|
NULL
|
|
);
|
|
RtlInitUnicodeString(
|
|
&Workstation,
|
|
NULL
|
|
);
|
|
*ApiSubStatus = STATUS_SUCCESS;
|
|
PasswordExpiry->LowPart = 0xffffffff;
|
|
PasswordExpiry->HighPart = 0x7fffffff;
|
|
*UserFlags = 0;
|
|
|
|
|
|
RtlZeroMemory(&PrimaryCredentials, sizeof(SECPKG_PRIMARY_CRED));
|
|
|
|
if (*ContextHandle == NULL)
|
|
{
|
|
// This is possibly an old style srv call (for 4.0 and before)
|
|
// so, alloc the context and replace the creds if new ones exists
|
|
|
|
fCallFromSrv = TRUE;
|
|
|
|
SspPrint((SSP_API_MORE, "SsprHandleAuthenticateMessage: *ContextHandle is NULL (old style SRV)\n"));
|
|
|
|
SECPKG_CALL_INFO CallInfo = {0};
|
|
|
|
//
|
|
// Client must have TCB, otherwise an un-trusted LSA-client could use a
|
|
// stolen challenge/response pair to network logon any user
|
|
//
|
|
|
|
if ( !LsaFunctions->GetCallInfo( &CallInfo ) ||
|
|
((CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0)
|
|
)
|
|
{
|
|
SecStatus = STATUS_PRIVILEGE_NOT_HELD;
|
|
SspPrint((SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: Client does not hold Tcb\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
SecStatus = SspCredentialReferenceCredential(
|
|
CredentialHandle,
|
|
FALSE,
|
|
&Credential );
|
|
|
|
if ( !NT_SUCCESS( SecStatus ) )
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: SspCredentialReferenceCredential returns %x.\n", SecStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
// check the validity of the NtlmAuthenticateMessage
|
|
|
|
if (SecondInputTokenSize < sizeof(NTLM_AUTHENTICATE_MESSAGE))
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: NtlmAuthenticateMessage size if bogus.\n" ));
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
// This is a superflous check since we alloc only if the caller
|
|
// has asked us too. This is to make sure that the srv always allocs
|
|
|
|
if (ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: ContextReqFlags has ISC_REQ_ALLOCATE_MEMORY.\n" ));
|
|
SecStatus = STATUS_NOT_SUPPORTED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (*OutputTokenSize < sizeof(NTLM_ACCEPT_RESPONSE))
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: NtlmAcceptResponse size if bogus.\n" ));
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a new context
|
|
//
|
|
|
|
Context = SspContextAllocateContext();
|
|
|
|
if (Context == NULL)
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: SspContextAllocateContext returns NULL.\n" ));
|
|
SecStatus = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// We've just added a context, we don't nornally add and then
|
|
// reference it.
|
|
|
|
SspContextDereferenceContext( Context );
|
|
|
|
*ContextHandle = (LSA_SEC_HANDLE) Context;
|
|
|
|
// Assign the Credential
|
|
|
|
Context->Credential = Credential;
|
|
Credential = NULL;
|
|
|
|
NtLmAuthenticateMessage = (PNTLM_AUTHENTICATE_MESSAGE) SecondInputToken;
|
|
if (NtLmAuthenticateMessage == NULL)
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: Error while assigning NtLmAuthenticateMessage\n" ));
|
|
SecStatus = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// copy challenge from NTLM_AUTHENTICATE_MESSAGE
|
|
RtlCopyMemory(Context->Challenge,
|
|
NtLmAuthenticateMessage->ChallengeToClient,
|
|
MSV1_0_CHALLENGE_LENGTH);
|
|
|
|
if (NtLmAuthenticateMessage->ParameterControl & MSV1_0_SUBAUTHENTICATION_FLAGS)
|
|
{
|
|
fSubAuth = TRUE;
|
|
SubAuthPackageId = (NtLmAuthenticateMessage->ParameterControl >>
|
|
MSV1_0_SUBAUTHENTICATION_DLL_SHIFT)
|
|
;
|
|
}
|
|
Context->State = ChallengeSentState;
|
|
Context->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE ;
|
|
|
|
//
|
|
// The server may request this option with a <= 4.0 client, in
|
|
// which case HandleNegotiateMessage, which normally sets
|
|
// this flag, won't have been called.
|
|
//
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_ALLOW_NON_USER_LOGONS ) != 0) {
|
|
|
|
*ContextAttributes |= ASC_RET_ALLOW_NON_USER_LOGONS;
|
|
Context->ContextFlags |= ASC_RET_ALLOW_NON_USER_LOGONS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the currently existing context.
|
|
//
|
|
|
|
SecStatus = SspContextReferenceContext( *ContextHandle, FALSE, &Context );
|
|
|
|
if ( !NT_SUCCESS(SecStatus) )
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: Error from SspContextReferenceContext.\n" ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
if ( ( Context->State != ChallengeSentState) &&
|
|
( Context->State != AuthenticatedState) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"Context not in ChallengeSentState\n" ));
|
|
SecStatus = SEC_E_OUT_OF_SEQUENCE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ignore the Credential Handle.
|
|
//
|
|
// Since this is the second call,
|
|
// the credential is implied by the Context.
|
|
// We could double check that the Credential Handle is either NULL or
|
|
// correct. However, our implementation doesn't maintain a close
|
|
// association between the two (actually no association) so checking
|
|
// would require a lot of overhead.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER( CredentialHandle );
|
|
|
|
//
|
|
// Get the AuthenticateMessage.
|
|
//
|
|
|
|
if ( InputTokenSize < sizeof(OLD_AUTHENTICATE_MESSAGE) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"AuthenticateMessage size wrong %ld\n",
|
|
InputTokenSize ));
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SecStatus = SspContextGetMessage( InputToken,
|
|
InputTokenSize,
|
|
NtLmAuthenticate,
|
|
(PVOID *)&AuthenticateMessage );
|
|
|
|
if ( !NT_SUCCESS(SecStatus) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"AuthenticateMessage GetMessage returns 0x%lx\n",
|
|
SecStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (fCallFromSrv)
|
|
{
|
|
// Copy the Context Negotiate Flags from what's sent in
|
|
Context->NegotiateFlags |= AuthenticateMessage->NegotiateFlags;
|
|
}
|
|
//
|
|
// If the call comes and we have already authenticated, then it is
|
|
// probably RPC trying to reauthenticate, which happens when someone
|
|
// calls two interfaces on the same connection. In this case we don't
|
|
// have to do anything - we just return success and let them get on
|
|
// with it. We do want to check that the input token is all zeros,
|
|
// though.
|
|
//
|
|
|
|
if ( Context->State == AuthenticatedState ) {
|
|
AUTHENTICATE_MESSAGE NullMessage;
|
|
|
|
*OutputTokenSize = 0;
|
|
|
|
//
|
|
// Check that all the fields are null. There are 5 strings
|
|
// in the Authenticate message that have to be set to zero.
|
|
//
|
|
|
|
RtlZeroMemory(&NullMessage.LmChallengeResponse, 5 * sizeof(STRING32));
|
|
|
|
if (memcmp(&AuthenticateMessage->LmChallengeResponse,
|
|
&NullMessage.LmChallengeResponse,
|
|
sizeof(STRING32) * 5) ) {
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"AuthenticateMessage->LmChallengeResponse is not zeroed\n"));
|
|
}
|
|
else
|
|
{
|
|
*ContextAttributes = SSP_RET_REAUTHENTICATION;
|
|
SecStatus = STATUS_SUCCESS;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If we are re-establishing a datagram context, get the negotiate flags
|
|
// out of this message.
|
|
//
|
|
|
|
if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0) {
|
|
|
|
if ((InputTokenSize < RTL_SIZEOF_THROUGH_FIELD(AUTHENTICATE_MESSAGE, NegotiateFlags)) ||
|
|
((AuthenticateMessage->NegotiateFlags &
|
|
NTLMSSP_NEGOTIATE_DATAGRAM) == 0) ) {
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Context->NegotiateFlags = AuthenticateMessage->NegotiateFlags;
|
|
|
|
//
|
|
// always do key exchange with datagram if we need a key (for SIGN or SEAL)
|
|
//
|
|
|
|
if (Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL))
|
|
{
|
|
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
|
}
|
|
|
|
//
|
|
// if got NTLM2, don't use LM_KEY
|
|
//
|
|
|
|
if ( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) != 0 )
|
|
{
|
|
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
|
|
SspPrint(( SSP_NEGOTIATE_FLAGS,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"AuthenticateMessage (datagram) NTLM2 caused LM_KEY to be disabled.\n" ));
|
|
}
|
|
|
|
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
|
}
|
|
|
|
if (AuthenticateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
|
|
{
|
|
if (InputTokenSize >= RTL_SIZEOF_THROUGH_FIELD(AUTHENTICATE_MESSAGE, Version))
|
|
{
|
|
C_ASSERT(sizeof(NTLM_VER_INFO) == sizeof(ULONG64));
|
|
|
|
RtlCopyMemory(&Context->ClientVersion, &AuthenticateMessage->Version, sizeof(AuthenticateMessage->Version));
|
|
|
|
SspPrint(( SSP_VERSION,
|
|
"SsprHandleAuthenticateMessage: ClientVersion %#I64x, Major %I64d, Minor %I64d, Build %I64d, Revision %I64d\n",
|
|
AuthenticateMessage->Version,
|
|
Context->ClientVersion.Major,
|
|
Context->ClientVersion.Minor,
|
|
Context->ClientVersion.Build,
|
|
Context->ClientVersion.Revision ));
|
|
|
|
}
|
|
else
|
|
{
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: AuthenticateMessage size too small with NTLMSSP_NEGOTIATE_VERSION\n" ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( (Context->ClientVersion.Revision >= NTLMSSP_REVISION_W2K3)
|
|
&& ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_LM_KEY | NTLMSSP_NEGOTIATE_NTLM2)) == 0) )
|
|
{
|
|
// smartclient knows to turn off LM KEY
|
|
|
|
SspPrint(( SSP_WARNING, "SsprHandleAuthenticateMessage client turned off LM keys\n" ));
|
|
IsDatagramLmKeyCorrectionOn = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check that client asked for minimum security required.
|
|
// not done for legacy down-level case, as, NegotiateFlags are
|
|
// constructed from incomplete information.
|
|
//
|
|
|
|
if ( !fCallFromSrv )
|
|
{
|
|
if (!SsprCheckMinimumSecurity(
|
|
AuthenticateMessage->NegotiateFlags,
|
|
NtLmGlobalMinimumServerSecurity)) {
|
|
|
|
SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"client didn't support minimum security requirements.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert relative pointers to absolute.
|
|
//
|
|
|
|
DoUnicode = ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) != 0;
|
|
|
|
if (!SspConvertRelativeToAbsolute(AuthenticateMessage,
|
|
InputTokenSize,
|
|
&AuthenticateMessage->LmChallengeResponse,
|
|
(PSTRING) &LmChallengeResponse,
|
|
FALSE, // No special alignment
|
|
TRUE ) ) { // NULL OK
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!SspConvertRelativeToAbsolute(AuthenticateMessage,
|
|
InputTokenSize,
|
|
&AuthenticateMessage->NtChallengeResponse,
|
|
(PSTRING) &NtChallengeResponse,
|
|
FALSE, // No special alignment
|
|
TRUE ) ) { // NULL OK
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!SspConvertRelativeToAbsolute(AuthenticateMessage,
|
|
InputTokenSize,
|
|
&AuthenticateMessage->DomainName,
|
|
&DomainName2,
|
|
DoUnicode, // Unicode alignment
|
|
TRUE ) ) { // NULL OK
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
|
|
InputTokenSize,
|
|
&AuthenticateMessage->UserName,
|
|
&UserName2,
|
|
DoUnicode, // Unicode alignment
|
|
#ifdef notdef
|
|
|
|
//
|
|
// Allow null sessions. The server should guard against them if
|
|
// it doesn't want them.
|
|
//
|
|
FALSE )) { // User name cannot be NULL
|
|
|
|
#endif // notdef
|
|
TRUE ) ) { // NULL OK
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
|
|
InputTokenSize,
|
|
&AuthenticateMessage->Workstation,
|
|
&Workstation2,
|
|
DoUnicode, // Unicode alignment
|
|
TRUE ) ) { // NULL OK
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this is datagram, get the session key
|
|
//
|
|
|
|
/// if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0) {
|
|
if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) != 0) {
|
|
|
|
if ( !SspConvertRelativeToAbsolute( AuthenticateMessage,
|
|
InputTokenSize,
|
|
&AuthenticateMessage->SessionKey,
|
|
&SessionKeyString,
|
|
FALSE, // No special alignment
|
|
TRUE) ) { // NULL OK
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// It should be NULL if this is a local call
|
|
//
|
|
|
|
if (((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL) == 0) &&
|
|
(SessionKeyString.Buffer == NULL)) {
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL)
|
|
{
|
|
static const UCHAR FixedSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH] = {
|
|
'S', 'y', 's', 't', 'e', 'm', 'L', 'i',
|
|
'b', 'r', 'a', 'r', 'y', 'D', 'T', 'C'
|
|
};
|
|
|
|
RtlCopyMemory(Context->SessionKey, FixedSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Convert the domainname/user name/workstation to the right character set.
|
|
//
|
|
|
|
if ( DoUnicode ) {
|
|
|
|
DomainName = *((PUNICODE_STRING) &DomainName2);
|
|
UserName = *((PUNICODE_STRING) &UserName2);
|
|
Workstation = *((PUNICODE_STRING) &Workstation2);
|
|
|
|
} else {
|
|
|
|
SspPrint(( SSP_API_MORE, "SsprHandleAuthenticateMessage: Not doing Unicode\n"));
|
|
Status = RtlOemStringToUnicodeString(
|
|
&DomainName,
|
|
&DomainName2,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SecStatus = SspNtStatusToSecStatus( Status,
|
|
SEC_E_INSUFFICIENT_MEMORY );
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlOemStringToUnicodeString(
|
|
&UserName,
|
|
&UserName2,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SecStatus = SspNtStatusToSecStatus( Status,
|
|
SEC_E_INSUFFICIENT_MEMORY );
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlOemStringToUnicodeString(
|
|
&Workstation,
|
|
&Workstation2,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SecStatus = SspNtStatusToSecStatus( Status,
|
|
SEC_E_INSUFFICIENT_MEMORY );
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Trace the username, domain name and workstation
|
|
//
|
|
if (NtlmGlobalEventTraceFlag){
|
|
|
|
//Header goo
|
|
SET_TRACE_HEADER(TraceInfo,
|
|
NtlmAcceptGuid,
|
|
EVENT_TRACE_TYPE_INFO,
|
|
WNODE_FLAG_TRACED_GUID|WNODE_FLAG_USE_MOF_PTR,
|
|
10);
|
|
|
|
int TraceHint = TRACE_ACCEPT_INFO;
|
|
SET_TRACE_DATA(TraceInfo,
|
|
TRACE_INITACC_STAGEHINT,
|
|
TraceHint);
|
|
|
|
SET_TRACE_DATAPTR(TraceInfo,
|
|
TRACE_INITACC_INCONTEXT,
|
|
TraceOldContextHandle);
|
|
|
|
SET_TRACE_DATAPTR(TraceInfo,
|
|
TRACE_INITACC_OUTCONTEXT,
|
|
ContextHandle);
|
|
|
|
// lets see the negotiate flags here
|
|
SET_TRACE_DATA(TraceInfo,
|
|
TRACE_INITACC_STATUS,
|
|
Context->NegotiateFlags);
|
|
|
|
SET_TRACE_USTRING(TraceInfo,
|
|
TRACE_INITACC_CLIENTNAME,
|
|
UserName);
|
|
|
|
SET_TRACE_USTRING(TraceInfo,
|
|
TRACE_INITACC_CLIENTDOMAIN,
|
|
DomainName);
|
|
|
|
SET_TRACE_USTRING(TraceInfo,
|
|
TRACE_INITACC_WORKSTATION,
|
|
Workstation);
|
|
|
|
TraceEvent(
|
|
NtlmGlobalTraceLoggerHandle,
|
|
(PEVENT_TRACE_HEADER)&TraceInfo
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// If the client is on the same machine as we are, just
|
|
// use the token the client has already placed in our context structure,
|
|
//
|
|
|
|
if ( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL ) &&
|
|
Context->TokenHandle != NULL &&
|
|
DomainName.Length == 0 &&
|
|
UserName.Length == 0 &&
|
|
Workstation.Length == 0 &&
|
|
AuthenticateMessage->NtChallengeResponse.Length == 0 &&
|
|
AuthenticateMessage->LmChallengeResponse.Length == 0 )
|
|
{
|
|
|
|
|
|
static const UCHAR FixedSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH] = {
|
|
'S', 'y', 's', 't', 'e', 'm', 'L', 'i',
|
|
'b', 'r', 'a', 'r', 'y', 'D', 'T', 'C'
|
|
};
|
|
|
|
LocalTokenHandle = Context->TokenHandle;
|
|
Context->TokenHandle = NULL;
|
|
|
|
KickOffTime.HighPart = 0x7FFFFFFF;
|
|
KickOffTime.LowPart = 0xFFFFFFFF;
|
|
|
|
RtlCopyMemory(Context->SessionKey, FixedSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
SspPrint(( SSP_MISC, "SsprHandleAuthenticateMessage: Local Call\n"));
|
|
|
|
if ( (ContextReqFlags & ASC_REQ_DELEGATE) )
|
|
{
|
|
//
|
|
// can support another hop if loopback.
|
|
//
|
|
|
|
*ContextAttributes |= ASC_RET_DELEGATE;
|
|
Context->ContextFlags |= ASC_RET_DELEGATE;
|
|
}
|
|
|
|
//
|
|
// If the client is on a different machine than we are,
|
|
// use LsaLogonUser to create a token for the client.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Store user name and domain name
|
|
//
|
|
|
|
SecStatus = NtLmDuplicateUnicodeString(
|
|
&Context->UserName,
|
|
&UserName);
|
|
if (!NT_SUCCESS(SecStatus)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
SecStatus = NtLmDuplicateUnicodeString(
|
|
&Context->DomainName,
|
|
&DomainName);
|
|
if (!NT_SUCCESS(SecStatus)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate an MSV1_0 network logon message
|
|
//
|
|
|
|
if (!fSubAuth)
|
|
{
|
|
//
|
|
// The string buffers may be used as structure pointers later on.
|
|
// Align them to pointer boundaries to avoid alignment problems.
|
|
//
|
|
|
|
MsvLogonMessageSize =
|
|
ROUND_UP_COUNT(sizeof(*MsvLogonMessage) +
|
|
DomainName.Length +
|
|
UserName.Length +
|
|
Workstation.Length, ALIGN_LPVOID) +
|
|
ROUND_UP_COUNT(AuthenticateMessage->NtChallengeResponse.Length, ALIGN_LPVOID) +
|
|
AuthenticateMessage->LmChallengeResponse.Length;
|
|
|
|
MsvLogonMessage = (PMSV1_0_LM20_LOGON)
|
|
NtLmAllocatePrivateHeap(MsvLogonMessageSize );
|
|
|
|
if ( MsvLogonMessage == NULL ) {
|
|
SecStatus = STATUS_NO_MEMORY;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: Error allocating MsvLogonMessage"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Build the MSV1_0 network logon message to pass to the LSA.
|
|
//
|
|
|
|
MsvLogonMessage->MessageType = MsV1_0NetworkLogon;
|
|
|
|
Where = (PCHAR)(MsvLogonMessage+1);
|
|
|
|
SspContextCopyStringAbsolute( MsvLogonMessage,
|
|
(PSTRING)&MsvLogonMessage->LogonDomainName,
|
|
(PSTRING)&DomainName,
|
|
&Where );
|
|
|
|
SspContextCopyStringAbsolute( MsvLogonMessage,
|
|
(PSTRING)&MsvLogonMessage->UserName,
|
|
(PSTRING)&UserName,
|
|
&Where );
|
|
|
|
SspContextCopyStringAbsolute( MsvLogonMessage,
|
|
(PSTRING)&MsvLogonMessage->Workstation,
|
|
(PSTRING)&Workstation,
|
|
&Where );
|
|
|
|
RtlCopyMemory( MsvLogonMessage->ChallengeToClient,
|
|
Context->Challenge,
|
|
sizeof( MsvLogonMessage->ChallengeToClient ) );
|
|
|
|
Where = (PCHAR) ROUND_UP_POINTER(Where, ALIGN_LPVOID);
|
|
SspContextCopyStringAbsolute( MsvLogonMessage,
|
|
&MsvLogonMessage->CaseSensitiveChallengeResponse,
|
|
&NtChallengeResponse,
|
|
&Where );
|
|
|
|
Where = (PCHAR) ROUND_UP_POINTER(Where, ALIGN_LPVOID);
|
|
SspContextCopyStringAbsolute(MsvLogonMessage,
|
|
&MsvLogonMessage->CaseInsensitiveChallengeResponse,
|
|
&LmChallengeResponse,
|
|
&Where );
|
|
|
|
MsvLogonMessage->ParameterControl = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
|
|
|
|
// This is required by the pre 4.0 server
|
|
if (fCallFromSrv)
|
|
{
|
|
MsvLogonMessage->ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED | NtLmAuthenticateMessage->ParameterControl;
|
|
|
|
if ( (Context->ContextFlags & ASC_RET_ALLOW_NON_USER_LOGONS ) != 0)
|
|
{
|
|
MsvLogonMessage->ParameterControl |= MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
|
|
}
|
|
} else {
|
|
MsvLogonMessage->ParameterControl |= MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
|
|
}
|
|
|
|
//
|
|
// Get the profile path for EFS
|
|
//
|
|
|
|
MsvLogonMessage->ParameterControl |= MSV1_0_RETURN_PROFILE_PATH;
|
|
|
|
//
|
|
// By passing in the RETURN_PASSWORD_EXPIRY flag, the password
|
|
// expiration time is returned in the logoff time
|
|
//
|
|
|
|
MsvLogonMessage->ParameterControl |= MSV1_0_RETURN_PASSWORD_EXPIRY;
|
|
|
|
//
|
|
// for Personal easy file/print sharing, hint to LsaLogonUser
|
|
// that Forced Guest may occur.
|
|
//
|
|
|
|
MsvLogonMessage->ParameterControl |= MSV1_0_ALLOW_FORCE_GUEST;
|
|
}
|
|
else
|
|
{
|
|
MsvSubAuthLogonMessageSize =
|
|
ROUND_UP_COUNT(sizeof(*MsvSubAuthLogonMessage) +
|
|
DomainName.Length +
|
|
UserName.Length +
|
|
Workstation.Length, ALIGN_LPVOID) +
|
|
ROUND_UP_COUNT(AuthenticateMessage->NtChallengeResponse.Length, ALIGN_LPVOID) +
|
|
AuthenticateMessage->LmChallengeResponse.Length;
|
|
|
|
MsvSubAuthLogonMessage = (PMSV1_0_SUBAUTH_LOGON)
|
|
NtLmAllocatePrivateHeap(MsvSubAuthLogonMessageSize );
|
|
|
|
if ( MsvSubAuthLogonMessage == NULL ) {
|
|
SecStatus = STATUS_NO_MEMORY;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: Error allocating MsvSubAuthLogonMessage"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Build the MSV1_0 subauth logon message to pass to the LSA.
|
|
//
|
|
|
|
MsvSubAuthLogonMessage->MessageType = MsV1_0SubAuthLogon;
|
|
|
|
Where = (PCHAR)(MsvSubAuthLogonMessage + 1);
|
|
|
|
SspContextCopyStringAbsolute( MsvSubAuthLogonMessage,
|
|
(PSTRING)&MsvSubAuthLogonMessage->LogonDomainName,
|
|
(PSTRING)&DomainName,
|
|
&Where );
|
|
|
|
SspContextCopyStringAbsolute( MsvSubAuthLogonMessage,
|
|
(PSTRING)&MsvSubAuthLogonMessage->UserName,
|
|
(PSTRING)&UserName,
|
|
&Where );
|
|
|
|
SspContextCopyStringAbsolute( MsvSubAuthLogonMessage,
|
|
(PSTRING)&MsvSubAuthLogonMessage->Workstation,
|
|
(PSTRING)&Workstation,
|
|
&Where );
|
|
|
|
RtlCopyMemory( MsvSubAuthLogonMessage->ChallengeToClient,
|
|
Context->Challenge,
|
|
sizeof( MsvSubAuthLogonMessage->ChallengeToClient ) );
|
|
|
|
Where = (PCHAR) ROUND_UP_POINTER(Where, ALIGN_LPVOID);
|
|
SspContextCopyStringAbsolute( MsvSubAuthLogonMessage,
|
|
&MsvSubAuthLogonMessage->AuthenticationInfo1,
|
|
&LmChallengeResponse,
|
|
&Where );
|
|
|
|
Where = (PCHAR) ROUND_UP_POINTER(Where, ALIGN_LPVOID);
|
|
SspContextCopyStringAbsolute(MsvSubAuthLogonMessage,
|
|
&MsvSubAuthLogonMessage->AuthenticationInfo2,
|
|
&NtChallengeResponse,
|
|
&Where );
|
|
|
|
MsvSubAuthLogonMessage->ParameterControl = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
|
|
|
|
MsvSubAuthLogonMessage->SubAuthPackageId = SubAuthPackageId;
|
|
|
|
// This is required by the pre 4.0 server
|
|
if (fCallFromSrv)
|
|
{
|
|
MsvSubAuthLogonMessage->ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED | NtLmAuthenticateMessage->ParameterControl;
|
|
}
|
|
|
|
if ( (Context->ContextFlags & ASC_RET_ALLOW_NON_USER_LOGONS ) != 0) {
|
|
MsvSubAuthLogonMessage->ParameterControl |= MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
|
|
}
|
|
|
|
//
|
|
// By passing in the RETURN_PASSWORD_EXPIRY flag, the password
|
|
// expiration time is returned in the logoff time
|
|
//
|
|
|
|
MsvSubAuthLogonMessage->ParameterControl |= MSV1_0_RETURN_PASSWORD_EXPIRY;
|
|
|
|
//
|
|
// for Personal easy file/print sharing, hint to LsaLogonUser
|
|
// that Forced Guest may occur.
|
|
//
|
|
|
|
MsvSubAuthLogonMessage->ParameterControl |= MSV1_0_ALLOW_FORCE_GUEST;
|
|
}
|
|
|
|
//
|
|
// if NTLM2 is negotiated, then mix my challenge with the client's...
|
|
// But, special case for null sessions. since we already negotiated
|
|
// NTLM2, but the LmChallengeResponse field is actually used
|
|
// here. REVIEW -- maybe don't negotiate NTLM2 if NULL session
|
|
//
|
|
|
|
if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) &&
|
|
(AuthenticateMessage->LmChallengeResponse.Length >= MSV1_0_CHALLENGE_LENGTH))
|
|
{
|
|
MsvLogonMessage->ParameterControl |= MSV1_0_USE_CLIENT_CHALLENGE;
|
|
}
|
|
|
|
//
|
|
// Log this user on.
|
|
//
|
|
|
|
// No origin (could use F(workstaion))
|
|
|
|
RtlInitString( &OriginName, NULL );
|
|
|
|
strncpy( SourceContext.SourceName,
|
|
"NtLmSsp ",
|
|
sizeof(SourceContext.SourceName) );
|
|
|
|
RtlZeroMemory( &SourceContext.SourceIdentifier,
|
|
sizeof(SourceContext.SourceIdentifier) );
|
|
|
|
{
|
|
PVOID TokenInformation;
|
|
PSECPKG_SUPPLEMENTAL_CRED_ARRAY Credentials = NULL;
|
|
|
|
if (!fSubAuth)
|
|
{
|
|
Status = LsaApLogonUserEx2(
|
|
(PLSA_CLIENT_REQUEST) (-1),
|
|
Network,
|
|
MsvLogonMessage,
|
|
MsvLogonMessage,
|
|
MsvLogonMessageSize,
|
|
(PVOID *) &LogonProfileMessage,
|
|
&LogonProfileMessageSize,
|
|
&LogonId,
|
|
&SubStatus,
|
|
&TokenInformationType,
|
|
&TokenInformation,
|
|
&AccountName,
|
|
&AuthenticatingAuthority,
|
|
&WorkstationName,
|
|
&PrimaryCredentials,
|
|
&Credentials
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Status = LsaApLogonUserEx2(
|
|
(PLSA_CLIENT_REQUEST) (-1),
|
|
Network,
|
|
MsvSubAuthLogonMessage,
|
|
MsvSubAuthLogonMessage,
|
|
MsvSubAuthLogonMessageSize,
|
|
(PVOID *) &LogonProfileMessage,
|
|
&LogonProfileMessageSize,
|
|
&LogonId,
|
|
&SubStatus,
|
|
&TokenInformationType,
|
|
&TokenInformation,
|
|
&AccountName,
|
|
&AuthenticatingAuthority,
|
|
&WorkstationName,
|
|
&PrimaryCredentials,
|
|
&Credentials
|
|
);
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"LsaApLogonUserEx2 returns 0x%lx for context 0x%x\n",
|
|
Status, Context ));
|
|
SecStatus = SspNtStatusToSecStatus( Status, SEC_E_LOGON_DENIED );
|
|
if (Status == STATUS_PASSWORD_MUST_CHANGE) {
|
|
*ApiSubStatus = Status;
|
|
}
|
|
else if (Status == STATUS_ACCOUNT_RESTRICTION) {
|
|
*ApiSubStatus = SubStatus;
|
|
} else {
|
|
*ApiSubStatus = Status;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(SubStatus) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"LsaApLogonUserEx2 returns SubStatus of 0x%lx\n",
|
|
SubStatus ));
|
|
SecStatus = SspNtStatusToSecStatus( SubStatus, SEC_E_LOGON_DENIED );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set package-specific flags in the logon session
|
|
//
|
|
|
|
if (fCallFromSrv)
|
|
{
|
|
I_LsaISetPackageAttrInLogonSession(&LogonId, LOGONSES_FLAG_NTLM_DOWNLEVEL);
|
|
}
|
|
|
|
//
|
|
// Check if this was a null session. The TokenInformationType will
|
|
// be LsaTokenInformationNull if it is. If so, we may need to fail
|
|
// the logon.
|
|
//
|
|
|
|
if (TokenInformationType == LsaTokenInformationNull)
|
|
{
|
|
|
|
//
|
|
// RESTRICT_NULL_SESSIONS deemed too risky because legacy behavior of package
|
|
// allows null sessions from SYSTEM.
|
|
//
|
|
|
|
#ifdef RESTRICT_NULL_SESSIONS
|
|
if ((Context->ContextFlags & ASC_REQ_ALLOW_NULL_SESSION) == 0) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"Null session logon attempted but not allowed\n" ));
|
|
SecStatus = SEC_E_LOGON_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
*ContextAttributes |= ASC_RET_NULL_SESSION;
|
|
Context->ContextFlags |= ASC_RET_NULL_SESSION;
|
|
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
|
|
|
|
AuditSid = NtLmGlobalAnonymousSid;
|
|
}
|
|
else
|
|
{
|
|
PLSA_TOKEN_INFORMATION_V2 TokenInfoV2 ;
|
|
|
|
TokenInfoV2 = (PLSA_TOKEN_INFORMATION_V2) TokenInformation ;
|
|
|
|
SafeAllocaAllocate( AllocatedAuditSid, RtlLengthSid( TokenInfoV2->User.User.Sid ) );
|
|
|
|
if ( AllocatedAuditSid )
|
|
{
|
|
|
|
RtlCopyMemory( AllocatedAuditSid,
|
|
TokenInfoV2->User.User.Sid,
|
|
RtlLengthSid( TokenInfoV2->User.User.Sid ) );
|
|
|
|
}
|
|
|
|
AuditSid = AllocatedAuditSid;
|
|
}
|
|
|
|
|
|
Status = LsaFunctions->CreateTokenEx(
|
|
&LogonId,
|
|
&SourceContext,
|
|
Network,
|
|
(((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) != 0) ?
|
|
SecurityIdentification : SecurityImpersonation),
|
|
TokenInformationType,
|
|
TokenInformation,
|
|
NULL,
|
|
WorkstationName,
|
|
((LogonProfileMessage->UserFlags & LOGON_PROFILE_PATH_RETURNED) != 0) ? &LogonProfileMessage->UserParameters : NULL,
|
|
&PrimaryCredentials,
|
|
SecSessionPrimaryCred,
|
|
&LocalTokenHandle,
|
|
&SubStatus);
|
|
|
|
if ( !NT_SUCCESS(Status) && (LocalTokenHandle == NULL) ) {
|
|
LsaFunctions->DeleteLogonSession(&LogonId);
|
|
}
|
|
|
|
if (Status == STATUS_ACCOUNT_RESTRICTION) {
|
|
*ApiSubStatus = SubStatus;
|
|
} else {
|
|
*ApiSubStatus = Status;
|
|
}
|
|
} // end of block for LsaLogonUser
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"CreateToken returns 0x%lx\n",
|
|
Status ));
|
|
|
|
SecStatus = Status;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(SubStatus) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"CreateToken returns SubStatus of 0x%lx\n",
|
|
SubStatus ));
|
|
SecStatus = SubStatus;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalTokenHandleOpenned = TRUE;
|
|
|
|
//
|
|
// Don't allow cleartext password on the logon.
|
|
// Except if called from Downlevel
|
|
|
|
if (!fCallFromSrv)
|
|
{
|
|
if ( LogonProfileMessage->UserFlags & LOGON_NOENCRYPTION ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"LsaLogonUser used cleartext password\n" ));
|
|
SecStatus = SEC_E_LOGON_DENIED;
|
|
goto Cleanup;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we did a guest logon, set the substatus to be STATUS_NO_SUCH_USER
|
|
//
|
|
|
|
if ( LogonProfileMessage->UserFlags & LOGON_GUEST ) {
|
|
fAvoidGuestAudit = TRUE;
|
|
*ApiSubStatus = STATUS_NO_SUCH_USER;
|
|
|
|
#if 0
|
|
//
|
|
// If caller required Sign/Seal, fail them here
|
|
//
|
|
if (
|
|
(!NtLmGlobalForceGuest) &&
|
|
(Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL)
|
|
)
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"LsaLogonUser logged user as a guest but seal is requested\n" ));
|
|
SecStatus = SEC_E_LOGON_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Save important information about the caller.
|
|
//
|
|
|
|
KickOffTime = LogonProfileMessage->KickOffTime;
|
|
|
|
//
|
|
// By passing in the RETURN_PASSWORD_EXPIRY flag, the password
|
|
// expiration time is returned in the logoff time
|
|
//
|
|
|
|
*PasswordExpiry = LogonProfileMessage->LogoffTime;
|
|
*UserFlags = LogonProfileMessage->UserFlags;
|
|
|
|
//
|
|
// set the session key to what the client sent us (if anything)
|
|
//
|
|
|
|
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH &&
|
|
AuthenticateMessage->SessionKey.Length == MSV1_0_USER_SESSION_KEY_LENGTH)
|
|
{
|
|
RtlCopyMemory(
|
|
Context->SessionKey,
|
|
SessionKeyString.Buffer,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
}
|
|
|
|
//
|
|
// Generate the session key, or decrypt the generated random one sent to
|
|
// us by the client, from various bits of info
|
|
//
|
|
|
|
SecStatus = SsprMakeSessionKey(
|
|
Context,
|
|
&LmChallengeResponse,
|
|
LogonProfileMessage->UserSessionKey,
|
|
LogonProfileMessage->LanmanSessionKey,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(SecStatus) ) {
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleAuthenticateMessage: "
|
|
"SsprMakeSessionKey failed.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Copy the logon domain name returned by the LSA if it is different.
|
|
// from the one the caller passed in. This may happen with temp duplicate
|
|
// accounts and local account
|
|
//
|
|
|
|
if ((LogonProfileMessage != NULL) &&
|
|
(LogonProfileMessage->LogonDomainName.Length != 0) &&
|
|
!RtlEqualUnicodeString(
|
|
&Context->DomainName,
|
|
&LogonProfileMessage->LogonDomainName,
|
|
TRUE // case insensitive
|
|
)) {
|
|
//
|
|
// erase the old domain name
|
|
//
|
|
|
|
if (Context->DomainName.Buffer != NULL) {
|
|
NtLmFreePrivateHeap(Context->DomainName.Buffer);
|
|
Context->DomainName.Buffer = NULL;
|
|
Context->DomainName.Length = Context->DomainName.MaximumLength = 0;
|
|
}
|
|
|
|
SecStatus = NtLmDuplicateUnicodeString(
|
|
&Context->DomainName,
|
|
&LogonProfileMessage->LogonDomainName
|
|
);
|
|
|
|
if (!NT_SUCCESS(SecStatus)) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// use SAM account username in case of UPN logon
|
|
//
|
|
|
|
if ((TokenInformationType != LsaTokenInformationNull)
|
|
&& (0 != PrimaryCredentials.DownlevelName.Length) // older DCs do not send DownlevelName for network logon
|
|
&& !RtlEqualUnicodeString(
|
|
&Context->UserName,
|
|
&PrimaryCredentials.DownlevelName,
|
|
TRUE // case insensitive
|
|
)) {
|
|
//
|
|
// erase the old user name
|
|
//
|
|
|
|
if (Context->UserName.Buffer != NULL) {
|
|
NtLmFreePrivateHeap(Context->UserName.Buffer);
|
|
Context->UserName.Buffer = NULL;
|
|
Context->UserName.Length = Context->UserName.MaximumLength = 0;
|
|
}
|
|
|
|
SecStatus = NtLmDuplicateUnicodeString(
|
|
&Context->UserName,
|
|
&PrimaryCredentials.DownlevelName
|
|
);
|
|
|
|
if (!NT_SUCCESS(SecStatus)) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allow the context to live until kickoff time.
|
|
//
|
|
|
|
SspContextSetTimeStamp( Context, KickOffTime );
|
|
|
|
//
|
|
// Return output parameters to the caller.
|
|
//
|
|
|
|
*ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
|
|
|
|
//
|
|
// Return output token
|
|
//
|
|
|
|
if (fCallFromSrv)
|
|
{
|
|
NtLmAcceptResponse = (PNTLM_ACCEPT_RESPONSE) *OutputToken;
|
|
if (NtLmAcceptResponse == NULL)
|
|
{
|
|
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LUID UNALIGNED * TempLogonId = (LUID UNALIGNED *) &NtLmAcceptResponse->LogonId;
|
|
*TempLogonId = LogonId;
|
|
NtLmAcceptResponse->UserFlags = LogonProfileMessage->UserFlags;
|
|
|
|
RtlCopyMemory(
|
|
NtLmAcceptResponse->UserSessionKey,
|
|
LogonProfileMessage->UserSessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
NtLmAcceptResponse->LanmanSessionKey,
|
|
LogonProfileMessage->LanmanSessionKey,
|
|
MSV1_0_LANMAN_SESSION_KEY_LENGTH
|
|
);
|
|
|
|
LARGE_INTEGER UNALIGNED *TempKickoffTime = (LARGE_INTEGER UNALIGNED *) &NtLmAcceptResponse->KickoffTime;
|
|
*TempKickoffTime = LogonProfileMessage->KickOffTime;
|
|
|
|
}
|
|
else
|
|
{
|
|
*OutputTokenSize = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// We don't support sign/seal options if fallback to Guest
|
|
// this is because the client and server won't have a matched session-key
|
|
// AND even if they did match (ie: blank password), the session-key
|
|
// would likely be well-known.
|
|
//
|
|
|
|
|
|
SecStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Free and locally used resources.
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Audit this logon
|
|
//
|
|
|
|
if (NT_SUCCESS(SecStatus)) {
|
|
|
|
//
|
|
// If we don't have an account name, this was a local connection
|
|
// and we didn't build a new token, so don't bother auditing.
|
|
// also, don't bother auditing logons that fellback to guest.
|
|
//
|
|
|
|
if ( (AccountName != NULL) &&
|
|
((AccountName->Length != 0) || (*ContextAttributes & ASC_RET_NULL_SESSION)) &&
|
|
!fAvoidGuestAudit ) {
|
|
|
|
LsaFunctions->AuditLogon(
|
|
STATUS_SUCCESS,
|
|
STATUS_SUCCESS,
|
|
AccountName,
|
|
AuthenticatingAuthority,
|
|
WorkstationName,
|
|
AuditSid,
|
|
Network,
|
|
&SourceContext,
|
|
&LogonId
|
|
);
|
|
}
|
|
} else {
|
|
LsaFunctions->AuditLogon(
|
|
!NT_SUCCESS(Status) ? Status : SecStatus,
|
|
SubStatus,
|
|
&UserName,
|
|
&DomainName,
|
|
&Workstation,
|
|
NULL,
|
|
Network,
|
|
&SourceContext,
|
|
&LogonId
|
|
);
|
|
|
|
}
|
|
|
|
if ( Context != NULL ) {
|
|
|
|
//
|
|
// client does not negotiate version, do not send the newer status code
|
|
//
|
|
|
|
if ( (STATUS_AUTHENTICATION_FIREWALL_FAILED == SecStatus)
|
|
&& (Context->ClientVersion.Revision < NTLMSSP_REVISION_W2K3_RC1) ) {
|
|
|
|
SspPrint(( SSP_WARNING,
|
|
"SsprHandleAuthenticateMessage: remapping STATUS_AUTHENTICATION_FIREWALL_FAILED to STATUS_NO_SUCH_USER\n",
|
|
SubStatus ));
|
|
|
|
SecStatus = STATUS_NO_SUCH_USER;
|
|
}
|
|
|
|
Context->Server = TRUE;
|
|
Context->LastStatus = SecStatus;
|
|
Context->DownLevel = fCallFromSrv;
|
|
|
|
|
|
//
|
|
// Don't allow this context to be used again.
|
|
//
|
|
|
|
if ( NT_SUCCESS(SecStatus) ) {
|
|
|
|
Context->State = AuthenticatedState;
|
|
|
|
if ( LocalTokenHandle ) {
|
|
*TokenHandle = LocalTokenHandle;
|
|
}
|
|
|
|
LocalTokenHandle = NULL;
|
|
|
|
RtlCopyMemory(
|
|
SessionKey,
|
|
Context->SessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH );
|
|
|
|
*NegotiateFlags = Context->NegotiateFlags;
|
|
|
|
//
|
|
// tricky here:
|
|
//
|
|
// datagram client side context is mapped during the first call, so
|
|
// remove LM key correction so that the mapped context on the
|
|
// server side would have matching flags
|
|
//
|
|
|
|
if (IsDatagramLmKeyCorrectionOn) {
|
|
|
|
SspPrint(( SSP_WARNING, "SsprHandleAuthenticateMessage turning on LM keys\n" ));
|
|
|
|
*NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
|
|
}
|
|
|
|
if ( !fAvoidGuestAudit ) {
|
|
|
|
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL ) {
|
|
*ContextAttributes |= ASC_RET_CONFIDENTIALITY;
|
|
}
|
|
|
|
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN ) {
|
|
*ContextAttributes |= ASC_RET_REPLAY_DETECT |
|
|
ASC_RET_SEQUENCE_DETECT |
|
|
ASC_RET_INTEGRITY;
|
|
}
|
|
|
|
if ( ContextReqFlags & ASC_REQ_REPLAY_DETECT ) {
|
|
*ContextAttributes |= ASC_RET_REPLAY_DETECT;
|
|
}
|
|
|
|
if ( ContextReqFlags & ASC_REQ_SEQUENCE_DETECT ) {
|
|
*ContextAttributes |= ASC_RET_SEQUENCE_DETECT;
|
|
}
|
|
}
|
|
|
|
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY ) {
|
|
*ContextAttributes |= ASC_RET_IDENTIFY;
|
|
}
|
|
|
|
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM ) {
|
|
*ContextAttributes |= ASC_RET_DATAGRAM;
|
|
}
|
|
|
|
if ( ContextReqFlags & ASC_REQ_ALLOW_NON_USER_LOGONS ) {
|
|
*ContextAttributes |= ASC_RET_ALLOW_NON_USER_LOGONS;
|
|
}
|
|
|
|
//
|
|
// if caller wants only INTEGRITY, then wants application
|
|
// supplied sequence numbers...
|
|
//
|
|
|
|
if ((Context->ContextFlags &
|
|
(ASC_REQ_INTEGRITY | ASC_REQ_REPLAY_DETECT | ASC_REQ_SEQUENCE_DETECT)) ==
|
|
ASC_REQ_INTEGRITY)
|
|
{
|
|
*NegotiateFlags |= NTLMSSP_APP_SEQ;
|
|
}
|
|
|
|
} else {
|
|
Context->State = IdleState;
|
|
}
|
|
|
|
// If we just created this context, then we need to dereference it
|
|
// once more with feeling
|
|
|
|
if (fCallFromSrv && !NT_SUCCESS(SecStatus))
|
|
{
|
|
PSSP_CONTEXT LocalContext;
|
|
SspContextReferenceContext (*ContextHandle, TRUE, &LocalContext);
|
|
ASSERT (LocalContext != NULL);
|
|
if (LocalContext != NULL)
|
|
{
|
|
SspContextDereferenceContext( LocalContext );
|
|
}
|
|
|
|
}
|
|
SspContextDereferenceContext( Context );
|
|
|
|
}
|
|
|
|
if ( NegotiateMessage != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( NegotiateMessage );
|
|
}
|
|
|
|
if ( AuthenticateMessage != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( AuthenticateMessage );
|
|
}
|
|
|
|
if ( MsvLogonMessage != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( MsvLogonMessage );
|
|
}
|
|
|
|
if ( MsvSubAuthLogonMessage != NULL ) {
|
|
(VOID) NtLmFreePrivateHeap( MsvSubAuthLogonMessage );
|
|
}
|
|
|
|
|
|
if ( LogonProfileMessage != NULL ) {
|
|
(VOID) LsaFunctions->FreeLsaHeap( LogonProfileMessage );
|
|
}
|
|
|
|
if ( LocalTokenHandle != NULL && LocalTokenHandleOpenned ) {
|
|
(VOID) NtClose( LocalTokenHandle );
|
|
}
|
|
|
|
if ( !DoUnicode ) {
|
|
if ( DomainName.Buffer != NULL) {
|
|
RtlFreeUnicodeString( &DomainName );
|
|
}
|
|
if ( UserName.Buffer != NULL) {
|
|
RtlFreeUnicodeString( &UserName );
|
|
}
|
|
if ( Workstation.Buffer != NULL) {
|
|
RtlFreeUnicodeString( &Workstation );
|
|
}
|
|
}
|
|
|
|
if (AccountName != NULL) {
|
|
if (AccountName->Buffer != NULL) {
|
|
LsaFunctions->FreeLsaHeap(AccountName->Buffer);
|
|
}
|
|
LsaFunctions->FreeLsaHeap(AccountName);
|
|
}
|
|
if (AuthenticatingAuthority != NULL) {
|
|
if (AuthenticatingAuthority->Buffer != NULL) {
|
|
LsaFunctions->FreeLsaHeap(AuthenticatingAuthority->Buffer);
|
|
}
|
|
LsaFunctions->FreeLsaHeap(AuthenticatingAuthority);
|
|
}
|
|
if (WorkstationName != NULL) {
|
|
if (WorkstationName->Buffer != NULL) {
|
|
LsaFunctions->FreeLsaHeap(WorkstationName->Buffer);
|
|
}
|
|
LsaFunctions->FreeLsaHeap(WorkstationName);
|
|
}
|
|
|
|
if ( AllocatedAuditSid )
|
|
{
|
|
SafeAllocaFree( AllocatedAuditSid );
|
|
}
|
|
|
|
//
|
|
// need to free the PrimaryCredentials fields filled in by LsaApLogonUserEx2
|
|
//
|
|
|
|
if ( PrimaryCredentials.DownlevelName.Buffer )
|
|
{
|
|
LsaFunctions->FreeLsaHeap(PrimaryCredentials.DownlevelName.Buffer);
|
|
}
|
|
|
|
if ( PrimaryCredentials.DomainName.Buffer )
|
|
{
|
|
LsaFunctions->FreeLsaHeap(PrimaryCredentials.DomainName.Buffer);
|
|
}
|
|
|
|
if ( PrimaryCredentials.UserSid )
|
|
{
|
|
LsaFunctions->FreeLsaHeap(PrimaryCredentials.UserSid);
|
|
}
|
|
|
|
if ( PrimaryCredentials.LogonServer.Buffer )
|
|
{
|
|
LsaFunctions->FreeLsaHeap(PrimaryCredentials.LogonServer.Buffer);
|
|
}
|
|
|
|
//
|
|
// Set a flag telling RPC not to destroy the connection yet
|
|
//
|
|
|
|
if (!NT_SUCCESS(SecStatus)) {
|
|
*ContextAttributes |= ASC_RET_THIRD_LEG_FAILED;
|
|
}
|
|
|
|
SspPrint(( SSP_API_MORE, "Leaving SsprHandleAutheticateMessage: 0x%lx\n", SecStatus ));
|
|
return SecStatus;
|
|
}
|
|
|