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

2270 lines
71 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

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

/*++
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;
if( NtLmSecPkg.MachineState & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED )
{
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;
}
//
// 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 = 0;
//
// 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 < sizeof(NEGOTIATE_MESSAGE)) {
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;
}
//
// Allocate a Challenge message
//
//
// always send target info -- new for NTLM3!
//
TargetFlags = NTLMSSP_NEGOTIATE_TARGET_INFO;
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
;
if (NtLmGlobalEncryptionEnabled) {
ChallengeMessage->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
}
}
//
// Build the Challenge Message
//
strcpy( (char *) ChallengeMessage->Signature, NTLMSSP_SIGNATURE );
ChallengeMessage->MessageType = NtLmChallenge;
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;
BOOLEAN DoUnicode = FALSE;
STRING DomainName2;
STRING UserName2;
STRING Workstation2;
STRING SessionKeyString;
UNICODE_STRING DomainName;
UNICODE_STRING UserName;
UNICODE_STRING Workstation;
LARGE_INTEGER KickOffTime;
LUID LogonId = {0};
HANDLE LocalTokenHandle = NULL;
BOOLEAN LocalTokenHandleOpenned = FALSE;
TOKEN_SOURCE SourceContext;
QUOTA_LIMITS Quotas;
NTSTATUS SubStatus;
STRING OriginName;
PCHAR Where;
UCHAR LocalSessionKey[LM_RESPONSE_LENGTH];
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;
//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)
)
{
Status = 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 < sizeof(AUTHENTICATE_MESSAGE)) ||
((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;
}
}
//
// 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;
LSA_TOKEN_INFORMATION_TYPE TokenInformationType;
LSA_TOKEN_INFORMATION_TYPE OriginalTokenType;
PSECPKG_SUPPLEMENTAL_CRED_ARRAY Credentials = NULL;
PPRIVILEGE_SET PrivilegesAssigned = 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;
}
//
// 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);
} // 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;
}
SecStatus = NtLmDuplicateUnicodeString(
&Context->DomainName,
&LogonProfileMessage->LogonDomainName
);
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.
//
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN ) {
*ContextAttributes |= ASC_RET_REPLAY_DETECT |
ASC_RET_SEQUENCE_DETECT |
ASC_RET_INTEGRITY;
}
if( !fAvoidGuestAudit )
{
if ( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL ) {
*ContextAttributes |= ASC_RET_CONFIDENTIALITY;
}
}
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY ) {
*ContextAttributes |= ASC_RET_IDENTIFY;
}
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM ) {
*ContextAttributes |= ASC_RET_DATAGRAM;
}
if ( ContextReqFlags & ASC_REQ_REPLAY_DETECT ) {
*ContextAttributes |= ASC_RET_REPLAY_DETECT;
}
if ( ContextReqFlags & ASC_REQ_SEQUENCE_DETECT ) {
*ContextAttributes |= ASC_RET_SEQUENCE_DETECT;
}
if ( ContextReqFlags & ASC_REQ_ALLOW_NON_USER_LOGONS ) {
*ContextAttributes |= ASC_RET_ALLOW_NON_USER_LOGONS;
}
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 ) {
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;
//
// 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;
}