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