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

3931 lines
121 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:
ctxtcli.cxx
Abstract:
API and support routines for handling security contexts.
Author:
Cliff Van Dyke (CliffV) 13-Jul-1993
Revision History:
ChandanS 03-Aug-1996 Stolen from net\svcdlls\ntlmssp\common\context.c
--*/
//
// Common include files.
//
#include <global.h>
#include <align.h> // ALIGN_WCHAR, etc
#include <credp.h>
#include "nlp.h"
NTSTATUS
SsprHandleFirstCall(
IN LSA_SEC_HANDLE CredentialHandle,
IN OUT PLSA_SEC_HANDLE ContextHandle,
IN ULONG ContextReqFlags,
IN ULONG InputTokenSize,
IN PVOID InputToken,
IN PUNICODE_STRING TargetServerName OPTIONAL,
IN OUT PULONG OutputTokenSize,
OUT PVOID *OutputToken,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime,
OUT PUCHAR SessionKey,
OUT PULONG NegotiateFlags
)
/*++
Routine Description:
Handle the First Call part of InitializeSecurityContext.
Arguments:
All arguments same as for InitializeSecurityContext
Return Value:
STATUS_SUCCESS -- All OK
SEC_I_CONTINUE_NEEDED -- Caller should call again later
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
--*/
{
SspPrint(( SSP_API_MORE, "Entering SsprHandleFirstCall\n" ));
NTSTATUS Status = STATUS_SUCCESS;
PSSP_CONTEXT Context = NULL;
PSSP_CREDENTIAL Credential = NULL;
PNEGOTIATE_MESSAGE NegotiateMessage = NULL;
ULONG NegotiateMessageSize = 0;
PCHAR Where = NULL;
ULONG NegotiateFlagsKeyStrength;
STRING NtLmLocalOemComputerNameString;
STRING NtLmLocalOemPrimaryDomainNameString;
//
// Initialization
//
*ContextAttributes = 0;
*NegotiateFlags = 0;
RtlInitString( &NtLmLocalOemComputerNameString, NULL );
RtlInitString( &NtLmLocalOemPrimaryDomainNameString, NULL );
//
// Get a pointer to the credential
//
Status = SspCredentialReferenceCredential(
CredentialHandle,
FALSE,
&Credential );
if ( !NT_SUCCESS( Status ) )
{
SspPrint(( SSP_CRITICAL, "SsprHandleFirstCall: invalid credential handle.\n" ));
goto Cleanup;
}
if ( (Credential->CredentialUseFlags & SECPKG_CRED_OUTBOUND) == 0 ) {
Status = SEC_E_INVALID_CREDENTIAL_USE;
SspPrint(( SSP_CRITICAL, "SsprHandleFirstCall: invalid credential use.\n" ));
goto Cleanup;
}
//
// Allocate a new context
//
Context = SspContextAllocateContext( );
if ( Context == NULL) {
Status = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL, "SsprHandleFirstCall: SspContextAllocateContext returned NULL\n"));
goto Cleanup;
}
//
// Build a handle to the newly created context.
//
*ContextHandle = (LSA_SEC_HANDLE) Context;
//
// We don't support any options.
//
// Complain about those that require we do something.
//
if ( (ContextReqFlags & ISC_REQ_PROMPT_FOR_CREDS) != 0 ) {
Status = SEC_E_INVALID_CONTEXT_REQ;
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: invalid ContextReqFlags 0x%lx.\n",
ContextReqFlags ));
goto Cleanup;
}
//
// Capture the default credentials from the credential structure.
//
if ( Credential->DomainName.Buffer != NULL ) {
Status = NtLmDuplicateUnicodeString(
&Context->DomainName,
&Credential->DomainName
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: NtLmDuplicateUnicodeString (DomainName) returned %d\n",Status));
goto Cleanup;
}
}
if ( Credential->UserName.Buffer != NULL ) {
Status = NtLmDuplicateUnicodeString(
&Context->UserName,
&Credential->UserName
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: NtLmDuplicateUnicodeString (UserName) returned %d\n", Status ));
goto Cleanup;
}
}
Status = SspCredentialGetPassword(
Credential,
&Context->Password
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: SspCredentialGetPassword returned %d\n", Status ));
goto Cleanup;
}
//
// save away any marshalled credential info.
//
Status = CredpExtractMarshalledTargetInfo(
TargetServerName,
&Context->TargetInfo
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: CredpExtractMarshalledTargetInfo returned %d\n", Status ));
goto Cleanup;
}
//
// Compute the negotiate flags
//
//
// Supported key strength(s)
//
NegotiateFlagsKeyStrength = NTLMSSP_NEGOTIATE_56;
if( NtLmSecPkg.MachineState & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED )
{
NegotiateFlagsKeyStrength |= NTLMSSP_NEGOTIATE_128;
}
Context->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_OEM |
NTLMSSP_NEGOTIATE_NTLM |
// ((NtLmGlobalLmProtocolSupported != 0)
// ? NTLMSSP_NEGOTIATE_NTLM2 : 0 ) |
NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NegotiateFlagsKeyStrength;
//
// NTLM2 session security is now the default request!
// allows us to support gss-style seal/unseal for LDAP.
//
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM2;
if ((ContextReqFlags & ISC_REQ_CONFIDENTIALITY) != 0) {
if (NtLmGlobalEncryptionEnabled) {
//
// CONFIDENTIALITY implies INTEGRITY
//
ContextReqFlags |= ISC_REQ_INTEGRITY;
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL |
NTLMSSP_NEGOTIATE_LM_KEY |
NTLMSSP_NEGOTIATE_KEY_EXCH ;
*ContextAttributes |= ISC_RET_CONFIDENTIALITY;
Context->ContextFlags |= ISC_RET_CONFIDENTIALITY;
} else {
Status = STATUS_NOT_SUPPORTED;
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: NtLmGlobalEncryptionEnabled is FALSE\n"));
goto Cleanup;
}
}
//
// If the caller specified INTEGRITY, SEQUENCE_DETECT or REPLAY_DETECT,
// that means they want to use the MakeSignature/VerifySignature
// calls. Add this to the negotiate.
//
if (ContextReqFlags &
(ISC_REQ_INTEGRITY | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT))
{
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN |
NTLMSSP_NEGOTIATE_KEY_EXCH |
NTLMSSP_NEGOTIATE_LM_KEY ;
}
if ((ContextReqFlags & ISC_REQ_INTEGRITY) != 0)
{
*ContextAttributes |= ISC_RET_INTEGRITY;
Context->ContextFlags |= ISC_RET_INTEGRITY;
}
if ((ContextReqFlags & ISC_REQ_SEQUENCE_DETECT) != 0)
{
*ContextAttributes |= ISC_RET_SEQUENCE_DETECT;
Context->ContextFlags |= ISC_RET_SEQUENCE_DETECT;
}
if ((ContextReqFlags & ISC_REQ_REPLAY_DETECT) != 0)
{
*ContextAttributes |= ISC_RET_REPLAY_DETECT;
Context->ContextFlags |= ISC_RET_REPLAY_DETECT;
}
if ( (ContextReqFlags & ISC_REQ_NULL_SESSION ) != 0) {
*ContextAttributes |= ISC_RET_NULL_SESSION;
Context->ContextFlags |= ISC_RET_NULL_SESSION;
}
if ( (ContextReqFlags & ISC_REQ_CONNECTION ) != 0) {
*ContextAttributes |= ISC_RET_CONNECTION;
Context->ContextFlags |= ISC_RET_CONNECTION;
}
//
// Check if the caller wants identify level
//
if ((ContextReqFlags & ISC_REQ_IDENTIFY)!= 0) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_IDENTIFY;
*ContextAttributes |= ISC_RET_IDENTIFY;
Context->ContextFlags |= ISC_RET_IDENTIFY;
}
IF_DEBUG( USE_OEM ) {
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_UNICODE;
}
if ( ((ContextReqFlags & ISC_REQ_MUTUAL_AUTH) != 0 ) &&
(NtLmGlobalMutualAuthLevel < 2 ) ) {
*ContextAttributes |= ISC_RET_MUTUAL_AUTH ;
if ( NtLmGlobalMutualAuthLevel == 0 )
{
Context->ContextFlags |= ISC_RET_MUTUAL_AUTH ;
}
}
//
// It is important to remove LM_KEY for compat 2 on the first call to ISC
// in the datagram case, but not harmful in the connection case
//
if (NtLmGlobalLmProtocolSupported == NoLm){
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
}
//
// For connection oriented security, we send a negotiate message to
// the server. For datagram, we get back the server's
// capabilities in the challenge message.
//
if ((ContextReqFlags & ISC_REQ_DATAGRAM) == 0) {
BOOLEAN CheckForLocal;
if ( (Credential->DomainName.Buffer == NULL &&
Credential->UserName.Buffer == NULL &&
Credential->Password.Buffer == NULL )
)
{
CheckForLocal = TRUE;
} else {
CheckForLocal = FALSE;
}
if( CheckForLocal ) {
//
// snap up a copy of the globals so we can just take the critsect once.
// the old way took the critsect twice, once to read sizes, second time
// to grab buffers - bad news if the global got bigger in between.
//
RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
if( NtLmGlobalOemComputerNameString.Buffer == NULL ||
NtLmGlobalOemPrimaryDomainNameString.Buffer == NULL ) {
//
// user has picked a computer name or domain name
// that failed to convert to OEM. disable the loopback
// detection.
// Sometime beyond Win2k, Negotiate package should have
// a general, robust scheme for detecting loopback.
//
CheckForLocal = FALSE;
} else {
Status = NtLmDuplicateString(
&NtLmLocalOemComputerNameString,
&NtLmGlobalOemComputerNameString
);
if( NT_SUCCESS(Status) ) {
Status = NtLmDuplicateString(
&NtLmLocalOemPrimaryDomainNameString,
&NtLmGlobalOemPrimaryDomainNameString
);
}
}
RtlReleaseResource(&NtLmGlobalCritSect);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: NtLmDuplicateUnicodeString (GlobalOemComputerName or GlobalOemPrimaryDomainName) returned %d\n", Status ));
goto Cleanup;
}
}
//
// Allocate a Negotiate message
//
NegotiateMessageSize = sizeof(*NegotiateMessage) +
NtLmLocalOemComputerNameString.Length +
NtLmLocalOemPrimaryDomainNameString.Length;
if ((ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if ( NegotiateMessageSize > *OutputTokenSize ) {
Status = SEC_E_BUFFER_TOO_SMALL;
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: OutputTokenSize is %d\n", *OutputTokenSize));
goto Cleanup;
}
}
NegotiateMessage = (PNEGOTIATE_MESSAGE)
NtLmAllocateLsaHeap( NegotiateMessageSize );
if ( NegotiateMessage == NULL) {
Status = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL, "SsprHandleFirstCall: Error allocating NegotiateMessage.\n"));
goto Cleanup;
}
//
// If this is the first call,
// build a Negotiate message.
//
strcpy( (char *) NegotiateMessage->Signature, NTLMSSP_SIGNATURE );
NegotiateMessage->MessageType = NtLmNegotiate;
NegotiateMessage->NegotiateFlags = Context->NegotiateFlags;
IF_DEBUG( REQUEST_TARGET ) {
NegotiateMessage->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
}
//
// Copy the DomainName and ComputerName into the negotiate message so
// the other side can determine if this is a call from the local system.
//
// Pass the names in the OEM character set since the character set
// hasn't been negotiated yet.
//
// Skip passing the workstation name if credentials were specified. This
// ensures the other side doesn't fall into the case that this is the
// local system. We wan't to ensure the new credentials are
// authenticated.
//
Where = (PCHAR)(NegotiateMessage+1);
if ( CheckForLocal ) {
SspContextCopyString( NegotiateMessage,
&NegotiateMessage->OemWorkstationName,
&NtLmLocalOemComputerNameString,
&Where );
NegotiateMessage->NegotiateFlags |=
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;
//
// OEM_DOMAIN_SUPPLIED used to always be supplied - but the
// only case it is ever used is when NTLMSSP_NEGOTIATE_LOCAL_CALL
// is set.
//
SspContextCopyString( NegotiateMessage,
&NegotiateMessage->OemDomainName,
&NtLmLocalOemPrimaryDomainNameString,
&Where );
NegotiateMessage->NegotiateFlags |=
NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;
}
if ((ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
RtlCopyMemory( *OutputToken,
NegotiateMessage,
NegotiateMessageSize );
}
else
{
*OutputToken = NegotiateMessage;
NegotiateMessage = NULL;
*ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
}
*OutputTokenSize = NegotiateMessageSize;
}
//
// Save a reference to the credential in the context.
//
Context->Credential = Credential;
Credential = NULL;
//
// Check for a caller requesting datagram security.
//
if ((ContextReqFlags & ISC_REQ_DATAGRAM) != 0) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_DATAGRAM;
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_NT_ONLY;
Context->ContextFlags |= ISC_RET_DATAGRAM;
*ContextAttributes |= ISC_RET_DATAGRAM;
// If datagram security is required, then we don't send back a token
*OutputTokenSize = 0;
//
// Generate a session key for this context if sign or seal was
// requested.
//
if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN |
NTLMSSP_NEGOTIATE_SEAL)) != 0) {
Status = SspGenerateRandomBits(
Context->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH
);
if( !NT_SUCCESS( Status ) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: SspGenerateRandomBits failed\n"));
goto Cleanup;
}
}
RtlCopyMemory(
SessionKey,
Context->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH
);
//
// Unless client wants to force its use,
// Turn off strong crypt, because we can't negotiate it.
//
if (!NtLmGlobalDatagramUse128BitEncryption) {
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_128;
}
//
// likewise for 56bit. note that package init handles turning
// off 56bit if 128bit is configured for datagram.
//
if(!NtLmGlobalDatagramUse56BitEncryption) {
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_56;
}
//
// Unless client wants to require NTLM2, can't use its
// message processing features because we start using
// MD5 sigs, full duplex mode, and datagram rekey before
// we know if the server supports NTLM2.
//
if (!NtLmGlobalRequireNtlm2) {
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_NTLM2;
}
//
// done fiddling with the negotiate flags, output them.
//
*NegotiateFlags = Context->NegotiateFlags;
//
// send back the negotiate flags to control signing and sealing
//
*NegotiateFlags |= NTLMSSP_APP_SEQ;
}
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH )
{
Status = SspGenerateRandomBits(
Context->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH
);
if( !NT_SUCCESS( Status ) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: SspGenerateRandomBits failed\n"));
goto Cleanup;
}
RtlCopyMemory(
SessionKey,
Context->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH
);
}
//
// Return output parameters to the caller.
//
*ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
Status = SEC_I_CONTINUE_NEEDED;
Context->State = NegotiateSentState;
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleFirstCall: NegotiateFlags = %lx\n", Context->NegotiateFlags));
//
// Check that caller asked for minimum security required.
//
if (!SsprCheckMinimumSecurity(
Context->NegotiateFlags,
NtLmGlobalMinimumClientSecurity)) {
Status = SEC_E_UNSUPPORTED_FUNCTION;
SspPrint(( SSP_CRITICAL,
"SsprHandleFirstCall: "
"Caller didn't request minimum security requirements (caller=0x%lx wanted=0x%lx).\n",
Context->NegotiateFlags, NtLmGlobalMinimumClientSecurity ));
goto Cleanup;
}
//
// 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) NtLmFreeLsaHeap( NegotiateMessage );
}
if ( Credential != NULL ) {
SspCredentialDereferenceCredential( Credential );
}
if ( NtLmLocalOemComputerNameString.Buffer != NULL ) {
(VOID) NtLmFreePrivateHeap( NtLmLocalOemComputerNameString.Buffer );
}
if ( NtLmLocalOemPrimaryDomainNameString.Buffer != NULL ) {
(VOID) NtLmFreePrivateHeap( NtLmLocalOemPrimaryDomainNameString.Buffer );
}
SspPrint(( SSP_API_MORE, "Leaving SsprHandleFirstCall: 0x%lx\n", Status ));
return Status;
UNREFERENCED_PARAMETER( InputToken );
UNREFERENCED_PARAMETER( InputTokenSize );
}
NTSTATUS
SsprHandleChallengeMessage(
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 PUNICODE_STRING TargetServerName,
IN OUT PULONG OutputTokenSize,
OUT PVOID *OutputToken,
IN OUT PULONG SecondOutputTokenSize,
OUT PVOID *SecondOutputToken,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime,
OUT PUCHAR SessionKey,
OUT PULONG NegotiateFlags
)
/*++
Routine Description:
Handle the Challenge message part of InitializeSecurityContext.
Arguments:
DomainName,UserName,Password - Passed in credentials to be used for this
context.
DomainNameSize,userNameSize,PasswordSize - length in characters of the
credentials to be used for this context.
SessionKey - Session key to use for this context
NegotiateFlags - Flags negotiated for this context
TargetServerName - Target server name, used by CredMgr to associates NT4 servers with domains
All other arguments same as for InitializeSecurityContext
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_NO_CREDENTIALS -- There are no credentials for this client
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus = STATUS_SUCCESS;
PSSP_CONTEXT Context = NULL;
PCHALLENGE_MESSAGE ChallengeMessage = NULL;
PNTLM_CHALLENGE_MESSAGE NtLmChallengeMessage = NULL;
PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL;
PNTLM_INITIALIZE_RESPONSE NtLmInitializeResponse = NULL;
PMSV1_0_GETCHALLENRESP_RESPONSE ChallengeResponseMessage = NULL;
STRING UserName = {0};
STRING DomainName = {0};
STRING Workstation = {0};
STRING LmChallengeResponse = {0};
STRING NtChallengeResponse = {0};
STRING DatagramSessionKey = {0};
BOOLEAN DoUnicode = TRUE;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS ProtocolStatus;
MSV1_0_GETCHALLENRESP_REQUEST TempChallengeResponse;
PMSV1_0_GETCHALLENRESP_REQUEST GetChallengeResponse;
ULONG GetChallengeResponseSize;
UNICODE_STRING RevealedPassword = {0};
ULONG ChallengeResponseSize;
ULONG AuthenticateMessageSize;
PCHAR Where;
UCHAR LocalSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
UCHAR DatagramKey[MSV1_0_USER_SESSION_KEY_LENGTH];
PLUID ClientLogonId = NULL;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel = SecurityImpersonation;
BOOLEAN UseSuppliedCreds = FALSE;
PSSP_CREDENTIAL Credential = NULL;
BOOLEAN fCallFromRedir = FALSE;
BOOLEAN fShareLevel = FALSE; // is target down-level share based security (no username) ?
BOOLEAN fCredmanCredentials = FALSE; // used credman creds?
UNICODE_STRING TargetName = {0};
UNICODE_STRING DefectiveTargetName = {0}; // for broken servers.
LPWSTR szCredTargetDomain = NULL;
LPWSTR szCredTargetServer = NULL;
LPWSTR szCredTargetDnsDomain = NULL;
LPWSTR szCredTargetDnsServer = NULL;
LPWSTR szCredTargetDnsTree = NULL;
LPWSTR szCredTargetPreDFSServer = NULL;
LUID LogonIdNetworkService = NETWORKSERVICE_LUID;
PSSP_CONTEXT ServerContext = NULL; // server context referenced during loopback
HANDLE ClientTokenHandle = NULL; // access token associated with client
// which called AcquireCredentialsHandle (OUTBOUND)
SspPrint((SSP_API_MORE, "Entering SsprHandleChallengeMessage\n"));
//
// Initialization
//
*ContextAttributes = 0;
*NegotiateFlags = 0;
GetChallengeResponse = &TempChallengeResponse;
if (*ContextHandle == NULL)
{
// This is possibly an old style redir call (for 4.0 and before)
// so, alloc the context and replace the creds if new ones exists
fCallFromRedir = TRUE;
SspPrint((SSP_API_MORE, "SsprHandleChallengeMessage: *ContextHandle is NULL (old-style RDR)\n"));
if ((ContextReqFlags & ISC_REQ_USE_SUPPLIED_CREDS) != 0)
{
UseSuppliedCreds = TRUE;
}
// This is a superflous check since we alloc only if the caller
// has asked us too. This is to make sure that the redir always asks us to alloc
if (!(ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY))
{
SecStatus = STATUS_NOT_SUPPORTED;
goto Cleanup;
}
SecStatus = SspCredentialReferenceCredential(
CredentialHandle,
FALSE,
&Credential );
if ( !NT_SUCCESS( SecStatus ) )
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: SspCredentialReferenceCredential returns %x.\n", SecStatus ));
goto Cleanup;
}
//
// Allocate a new context
//
Context = SspContextAllocateContext();
if (Context == NULL)
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: 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;
//
// Capture the default credentials from the credential structure.
//
if ( Credential->DomainName.Buffer != NULL ) {
Status = NtLmDuplicateUnicodeString(
&Context->DomainName,
&Credential->DomainName
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmDuplicateUnicodeString (DomainName) returned %d\n",Status));
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY);
goto Cleanup;
}
}
if ( Credential->UserName.Buffer != NULL ) {
Status = NtLmDuplicateUnicodeString(
&Context->UserName,
&Credential->UserName
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmDuplicateUnicodeString (UserName) returned %d\n", Status ));
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY);
goto Cleanup;
}
}
SecStatus = SspCredentialGetPassword(
Credential,
&Context->Password
);
if (!NT_SUCCESS(SecStatus)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: SspCredentialGetPassword returned %d\n", SecStatus ));
goto Cleanup;
}
// Assign the Credential
Context->Credential = Credential;
Credential = NULL;
//
// fake it
//
Context->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_OEM |
NTLMSSP_REQUEST_TARGET |
NTLMSSP_REQUEST_INIT_RESPONSE |
((NtLmGlobalLmProtocolSupported != 0)
? NTLMSSP_NEGOTIATE_NTLM2 : 0 ) |
NTLMSSP_TARGET_TYPE_SERVER ;
*ExpirationTime = SspContextGetTimeStamp(Context, TRUE);
Context->State = NegotiateSentState;
// If creds are passed in by the RDR, then replace the ones in the context
if (UseSuppliedCreds)
{
if (SecondInputTokenSize < sizeof(NTLM_CHALLENGE_MESSAGE))
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Invalid SecondInputTokensize.\n" ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
NtLmChallengeMessage = (PNTLM_CHALLENGE_MESSAGE) NtLmAllocatePrivateHeap(SecondInputTokenSize);
if (NtLmChallengeMessage == NULL)
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Error while allocating NtLmChallengeMessage\n" ));
SecStatus = STATUS_NO_MEMORY;
goto Cleanup;
}
RtlCopyMemory(NtLmChallengeMessage,
SecondInputToken,
SecondInputTokenSize);
//
// NULL session is only true if user, domain, and password are all
// empty strings in stead of NULLs
//
if (((NtLmChallengeMessage->Password.Length == 0) && (NtLmChallengeMessage->Password.Buffer != NULL)) &&
((NtLmChallengeMessage->UserName.Length == 0) && (NtLmChallengeMessage->UserName.Buffer != NULL)) &&
((NtLmChallengeMessage->DomainName.Length == 0) && (NtLmChallengeMessage->DomainName.Buffer != NULL)))
{
// This could only be a null session request
SspPrint(( SSP_WARNING, "SsprHandleChallengeMessage: null session NtLmChallengeMessage\n" ));
if (Context->Password.Buffer != NULL)
{
// free it first
NtLmFreePrivateHeap (Context->Password.Buffer);
}
Context->Password.Buffer = (LPWSTR) NtLmAllocatePrivateHeap(sizeof(WCHAR));
if (Context->Password.Buffer == NULL)
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmAllocatePrivateHeap(Password) returns NULL.\n"));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
Context->Password.Length = 0;
Context->Password.MaximumLength = 0;
*(Context->Password.Buffer) = L'\0';
SspHidePassword(&Context->Password);
if (Context->UserName.Buffer != NULL)
{
// free it first
NtLmFreePrivateHeap (Context->UserName.Buffer);
}
Context->UserName.Buffer = (LPWSTR) NtLmAllocatePrivateHeap(sizeof(WCHAR));
if (Context->UserName.Buffer == NULL)
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmAllocatePrivateHeap(UserName) returns NULL.\n"));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
Context->UserName.Length = 0;
Context->UserName.MaximumLength = sizeof(WCHAR);
*(Context->UserName.Buffer) = L'\0';
if (Context->DomainName.Buffer != NULL)
{
// free it first
NtLmFreePrivateHeap (Context->DomainName.Buffer);
}
Context->DomainName.Buffer = (LPWSTR) NtLmAllocatePrivateHeap(sizeof(WCHAR));
if (Context->DomainName.Buffer == NULL)
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmAllocatePrivateHeap(DomainName) returns NULL.\n"));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
Context->DomainName.Length = 0;
Context->DomainName.MaximumLength = sizeof(WCHAR);
*(Context->DomainName.Buffer) = L'\0';
}
else
{
ULONG_PTR BufferTail = (ULONG_PTR)NtLmChallengeMessage + SecondInputTokenSize;
UNICODE_STRING AbsoluteString;
if (NtLmChallengeMessage->Password.Buffer != 0)
{
AbsoluteString.Buffer = (LPWSTR)((PUCHAR)NtLmChallengeMessage + NtLmChallengeMessage->Password.Buffer);
//
// verify buffer not out of range.
//
if( ( (ULONG_PTR)AbsoluteString.Buffer > BufferTail ) ||
( (ULONG_PTR)((PUCHAR)AbsoluteString.Buffer + NtLmChallengeMessage->Password.Length) > BufferTail ) ||
( (ULONG_PTR)AbsoluteString.Buffer < (ULONG_PTR)NtLmChallengeMessage )
)
{
SecStatus = SEC_E_NO_CREDENTIALS;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Buffer overflow (Password).\n" ));
goto Cleanup;
}
if (Context->Password.Buffer != NULL)
{
// free it first
NtLmFreePrivateHeap (Context->Password.Buffer);
Context->Password.Buffer = NULL;
}
AbsoluteString.Length = AbsoluteString.MaximumLength = NtLmChallengeMessage->Password.Length;
SecStatus = NtLmDuplicatePassword(
&Context->Password,
&AbsoluteString
);
if (!NT_SUCCESS(SecStatus))
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmDuplicatePassword returns 0x%lx.\n",SecStatus ));
goto Cleanup;
}
SspHidePassword(&Context->Password);
}
if (NtLmChallengeMessage->UserName.Length != 0)
{
AbsoluteString.Buffer = (LPWSTR)((PUCHAR)NtLmChallengeMessage + NtLmChallengeMessage->UserName.Buffer);
//
// verify buffer not out of range.
//
if( ( (ULONG_PTR)AbsoluteString.Buffer > BufferTail ) ||
( (ULONG_PTR)((PUCHAR)AbsoluteString.Buffer + NtLmChallengeMessage->UserName.Length) > BufferTail ) ||
( (ULONG_PTR)AbsoluteString.Buffer < (ULONG_PTR)NtLmChallengeMessage )
)
{
SecStatus = SEC_E_NO_CREDENTIALS;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Buffer overflow (UserName).\n" ));
goto Cleanup;
}
if (Context->UserName.Buffer != NULL)
{
// free it first
NtLmFreePrivateHeap (Context->UserName.Buffer);
}
AbsoluteString.Length = AbsoluteString.MaximumLength = NtLmChallengeMessage->UserName.Length;
SecStatus = NtLmDuplicateUnicodeString(&Context->UserName,
&AbsoluteString);
if (!NT_SUCCESS(SecStatus))
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmDuplicateUnicodeString(UserName) returns 0x%lx.\n",SecStatus ));
goto Cleanup;
}
}
if (NtLmChallengeMessage->DomainName.Length != 0)
{
AbsoluteString.Buffer = (LPWSTR)((PUCHAR)NtLmChallengeMessage + NtLmChallengeMessage->DomainName.Buffer);
//
// verify buffer not out of range.
//
if( ( (ULONG_PTR)AbsoluteString.Buffer > BufferTail ) ||
( (ULONG_PTR)((PUCHAR)AbsoluteString.Buffer + NtLmChallengeMessage->DomainName.Length) > BufferTail ) ||
( (ULONG_PTR)AbsoluteString.Buffer < (ULONG_PTR)NtLmChallengeMessage )
)
{
SecStatus = SEC_E_NO_CREDENTIALS;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Buffer overflow (DomainName).\n" ));
goto Cleanup;
}
if (Context->DomainName.Buffer != NULL)
{
// free it first
NtLmFreePrivateHeap (Context->DomainName.Buffer);
}
AbsoluteString.Length = AbsoluteString.MaximumLength = NtLmChallengeMessage->DomainName.Length;
SecStatus = NtLmDuplicateUnicodeString(&Context->DomainName,
&AbsoluteString);
if (!NT_SUCCESS(SecStatus))
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmDuplicateUnicodeString(DomainName) returns 0x%lx.\n",SecStatus ));
goto Cleanup;
}
}
}
if (NtLmChallengeMessage)
{
NtLmFreePrivateHeap (NtLmChallengeMessage);
NtLmChallengeMessage = NULL;
}
} // end of special casing if credentials are supplied in the first init call
} // end of special casing for the old style redir
//
// Find the currently existing context.
//
SecStatus = SspContextReferenceContext( *ContextHandle, FALSE, &Context );
if ( !NT_SUCCESS(SecStatus) )
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: invalid context handle.\n" ));
goto Cleanup;
}
//
// bug 321061: passing Accept handle to Init causes AV.
//
if( Context->Credential == NULL ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: invalid context handle, missing credential.\n" ));
SecStatus = STATUS_INVALID_HANDLE;
goto Cleanup;
}
//
// If this is not reauthentication (or is datagram reauthentication)
// pull the context out of the associated credential.
//
if ((Context->State != AuthenticateSentState) ||
(Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0)
{
ClientLogonId = &Context->Credential->LogonId;
ImpersonationLevel = Context->Credential->ImpersonationLevel;
}
//
// process the TargetServerName to see if marshalled target info was
// passed in. This can happen if the caller passes in marshalled target
// info for each call to InitializeSecurityContext(), or, uses the downlevel
// RDR path which happened previously in this routine.
//
Status = CredpExtractMarshalledTargetInfo(
TargetServerName,
&Context->TargetInfo
);
if (!NT_SUCCESS(Status)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: CredpExtractMarshalledTargetInfo returned %d\n", Status ));
goto Cleanup;
}
//
// If we have already sent the authenticate message, then this must be
// RPC calling Initialize a third time to re-authenticate a connection.
// This happens when a new interface is called over an existing
// connection. What we do here is build a NULL authenticate message
// that the server will recognize and also ignore.
//
//
// That being said, if we are doing datagram style authentication then
// the story is different. The server may have dropped this security
// context and then the client sent another packet over. The server
// will then be trying to restore the context, so we need to build
// another authenticate message.
//
if ( Context->State == AuthenticateSentState ) {
AUTHENTICATE_MESSAGE NullMessage;
if (((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) ==
NTLMSSP_NEGOTIATE_DATAGRAM) &&
(InputTokenSize != 0) &&
(InputToken != NULL) ) {
//
// we are doing a reauthentication for datagram, so let this
// through. We don't want the security.dll remapping this
// context.
//
*ContextAttributes |= SSP_RET_REAUTHENTICATION;
} else {
//
// To make sure this is the intended meaning of the call, check
// that the input token is NULL.
//
if ( (InputTokenSize != 0) || (InputToken != NULL) ) {
SecStatus = SEC_E_INVALID_TOKEN;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: (re-auth) invalid InputTokenSize.\n" ));
goto Cleanup;
}
if ( (*OutputTokenSize < sizeof(NullMessage)) &&
((ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0))
{
SecStatus = SEC_E_BUFFER_TOO_SMALL;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: invalid OutputTokenSize.\n" ));
}
else {
RtlZeroMemory( &NullMessage, sizeof(NullMessage) );
strcpy( (char *)NullMessage.Signature, NTLMSSP_SIGNATURE );
NullMessage.MessageType = NtLmAuthenticate;
if ((ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
RtlCopyMemory( *OutputToken,
&NullMessage,
sizeof(NullMessage));
}
else
{
PAUTHENTICATE_MESSAGE NewNullMessage = (PAUTHENTICATE_MESSAGE)
NtLmAllocateLsaHeap(sizeof(NullMessage));
if ( NewNullMessage == NULL)
{
SecStatus = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Error allocating NewNullMessage.\n" ));
goto Cleanup;
}
*OutputToken = NewNullMessage;
NewNullMessage = NULL;
*ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
}
*OutputTokenSize = sizeof(NullMessage);
}
*ContextAttributes |= SSP_RET_REAUTHENTICATION;
goto Cleanup;
}
} else if ( Context->State != NegotiateSentState ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"Context not in NegotiateSentState\n" ));
SecStatus = SEC_E_OUT_OF_SEQUENCE;
goto Cleanup;
}
//
// We don't support any options.
// Complain about those that require we do something.
//
if ( (ContextReqFlags & ISC_REQ_PROMPT_FOR_CREDS) != 0 ){
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: invalid ContextReqFlags 0x%lx.\n",
ContextReqFlags ));
SecStatus = SEC_E_INVALID_CONTEXT_REQ;
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 ChallengeMessage.
//
if ( InputTokenSize < sizeof(OLD_CHALLENGE_MESSAGE) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage size wrong %ld\n",
InputTokenSize ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
SecStatus = SspContextGetMessage( InputToken,
InputTokenSize,
NtLmChallenge,
(PVOID *)&ChallengeMessage );
if ( !NT_SUCCESS(SecStatus) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage GetMessage returns 0x%lx\n",
SecStatus ));
goto Cleanup;
}
//
// for down-level RDR, EXPORTED_CONTEXT is a hint that we are talking to
// share level target.
//
if( fCallFromRedir )
{
if( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_EXPORTED_CONTEXT )
{
ChallengeMessage->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_EXPORTED_CONTEXT);
fShareLevel = TRUE;
SspPrint(( SSP_WARNING,
"SsprHandleChallengeMessage: "
"downlevel sharelevel security target\n"));
}
}
if( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM ) {
//
// take out any flags we didn't ask for -- self defense from bogus combinations
//
ChallengeMessage->NegotiateFlags &=
( Context->NegotiateFlags |
NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_TARGET_TYPE_SERVER |
NTLMSSP_TARGET_TYPE_DOMAIN |
NTLMSSP_NEGOTIATE_LOCAL_CALL );
}
//
// Determine if the caller wants OEM or UNICODE
//
if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_OEM;
DoUnicode = TRUE;
} else if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_OEM ){
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_UNICODE;
DoUnicode = FALSE;
} else {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage bad NegotiateFlags 0x%lx\n",
ChallengeMessage->NegotiateFlags ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Copy other interesting negotiate flags into the context
//
if( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO ) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
} else {
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_TARGET_INFO);
}
//
// if got NTLM2, or if LM_KEY specifically forbidden don't use LM_KEY
//
if ((ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) ||
(NtLmGlobalLmProtocolSupported == NoLm)) {
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: "
"Server support NTLM2 caused LM_KEY to be disabled.\n" ));
}
ChallengeMessage->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_LM_KEY);
}
//
// if we did not get NTLM2 remove it from context negotiate flags
//
if(!(ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)){
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2 ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: "
"Server didn't support NTLM2 and client did.\n" ));
}
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_NTLM2);
}
if ((ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM) == 0) {
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: "
"Server didn't support NTLM and client did.\n" ));
}
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_NTLM);
}
if ((ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) == 0) {
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: "
"Server didn't support KEY_EXCH and client did.\n" ));
}
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_KEY_EXCH);
}
if ((ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) == 0) {
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: "
"Server didn't support LM_KEY and client did.\n" ));
}
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_LM_KEY);
}
//
// make sure KEY_EXCH is always set if DATAGRAM negotiated and we need a key
// this is for local internal use; its now safe because we've got the bits
// to go on the wire copied...
//
if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) &&
(Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN |NTLMSSP_NEGOTIATE_SEAL)))
{
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
}
//
// allow negotiate of certain options such as sign/seal when server
// asked for it, but client didn't.
//
#if 0
////
if( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL ) {
if( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) == 0 ) {
SspPrint(( SSP_SESSION_KEYS,
"SsprHandleChallengeMessage: client didn't request SEAL but server did, adding SEAL.\n"));
}
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
}
if( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN ) {
if( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) == 0 ) {
SspPrint(( SSP_SESSION_KEYS,
"SsprHandleChallengeMessage: client didn't request SIGN but server did, adding SIGN.\n"));
}
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
}
////
#endif
//
// if server didn't support certain crypto strengths, insure they
// are disabled.
//
if( (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_56) == 0 ) {
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_56 ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: Client supported 56, but server didn't.\n"));
}
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_56);
}
if( (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_128) == 0 ) {
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_128 ) {
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: Client supported 128, but server didn't.\n"));
}
Context->NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_128);
}
//
// Check that server gave minimum security required.
// not done for legacy down-level case, as, NegotiateFlags are
// constructed from incomplete information.
//
if( !fCallFromRedir )
{
if (!SsprCheckMinimumSecurity(
Context->NegotiateFlags,
NtLmGlobalMinimumClientSecurity)) {
SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage didn't support minimum security requirements.\n" ));
goto Cleanup;
}
}
if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN ) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
} else {
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
}
//
// Determine that the caller negotiated to NTLM or nothing, but not
// NetWare.
//
if ( (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NETWARE) &&
((ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM) == 0) &&
((ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) == 0)
) {
SecStatus = STATUS_NOT_SUPPORTED;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage asked for Netware only.\n" ));
goto Cleanup;
}
//
// Check if we negotiated for identify level
//
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) {
if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) {
Context->ContextFlags |= ISC_REQ_IDENTIFY;
*ContextAttributes |= ISC_RET_IDENTIFY;
} else {
SecStatus = STATUS_NOT_SUPPORTED;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage bad NegotiateFlags 0x%lx\n",
ChallengeMessage->NegotiateFlags ));
goto Cleanup;
}
}
//
// If the server is running on this same machine,
// just duplicate our caller's token and use it.
//
while ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL )
{
ULONG_PTR ServerContextHandle;
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'
};
SspPrint(( SSP_MISC,
"SsprHandleChallengeMessage: Local Call.\n"));
//
// Require the new challenge message if we are going to access the
// server context handle
//
if ( InputTokenSize < sizeof(CHALLENGE_MESSAGE) ) {
SecStatus = SEC_E_INVALID_TOKEN;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: invalid InputTokenSize.\n"));
goto Cleanup;
}
//
// Open the server's context here within this process.
//
ServerContextHandle = (ULONG_PTR)(ChallengeMessage->ServerContextHandle);
SspContextReferenceContext(
ServerContextHandle,
FALSE,
&ServerContext
);
if ( ServerContext == NULL )
{
//
// This means the server has lied about this being a local call or
// the server process has exitted.
// this can happen if the client and server have not had netbios
// machine names set, so, allow this and continue processing
// as if this were not loopback.
//
SspPrint(( SSP_WARNING,
"SsprHandleChallengeMessage: "
"ChallengeMessage bad ServerContextHandle 0x%p\n",
ChallengeMessage->ServerContextHandle));
ChallengeMessage->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LOCAL_CALL;
break;
}
if(((ServerContext->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL) == 0) ||
(ServerContext->State != ChallengeSentState)
)
{
SspPrint(( SSP_WARNING,
"SsprHandleChallengeMessage: "
"ChallengeMessage claimed ServerContextHandle in bad state 0x%p\n",
ChallengeMessage->ServerContextHandle));
ChallengeMessage->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LOCAL_CALL;
break;
}
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_LOCAL_CALL;
//
// Local loopback for network servcice
//
if ( (Context->Credential->MutableCredFlags & SSP_CREDENTIAL_FLAG_WAS_NETWORK_SERVICE) )
{
SspPrint((SSP_WARNING, "SsprHandleChallengeMessage using networkservice in local loopback\n"));
ClientLogonId = &LogonIdNetworkService;
}
//
// open the token associated with the caller at the time of the
// AcquireCredentialsHandle() call.
//
SecStatus = LsaFunctions->OpenTokenByLogonId(
ClientLogonId,
&ClientTokenHandle
);
if(!NT_SUCCESS(SecStatus))
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"Could not open client token 0x%lx\n",
SecStatus ));
goto Cleanup;
}
if( ImpersonationLevel < SecurityImpersonation )
{
SspPrint(( SSP_WARNING, "Reducing impersonation level %lu to %lu\n",
SecurityImpersonation, ImpersonationLevel));
}
if ((Context->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) != 0) {
ImpersonationLevel = min(SecurityIdentification, ImpersonationLevel);
}
SecStatus = SspDuplicateToken(
ClientTokenHandle,
ImpersonationLevel,
&ServerContext->TokenHandle
);
if (!NT_SUCCESS(SecStatus)) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"Could not duplicate client token 0x%lx\n",
SecStatus ));
goto Cleanup;
}
//
// enable all privileges in the duplicated token, to be consistent
// with what a network logon normally looks like
// (all privileges held are enabled by default).
//
if(!SspEnableAllPrivilegesToken(ServerContext->TokenHandle))
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"Could not enable all privileges for loopback.\n"));
}
//
// give local call a hard-coded session key so calls into RDR
// to fetch a session key succeed (eg: RtlGetUserSessionKeyClient)
//
RtlCopyMemory(Context->SessionKey, FixedSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
//
// Don't pass any credentials in the authenticate message.
//
RtlInitString( &DomainName, NULL );
RtlInitString( &UserName, NULL );
RtlInitString( &Workstation, NULL );
RtlInitString( &NtChallengeResponse, NULL );
RtlInitString( &LmChallengeResponse, NULL );
RtlInitString( &DatagramSessionKey, NULL );
break;
}
//
// If the server is running on a diffent machine,
// determine the caller's DomainName, UserName and ChallengeResponse
// to pass back in the AuthenicateMessage.
//
if ( (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL) == 0 )
{
//
// Build the GetChallengeResponse message to pass to the LSA.
//
PCHAR MarshalPtr; // marshalling pointer
ULONG MarshalBytes;
UNICODE_STRING TargetInfo;
CREDENTIAL_TARGET_INFORMATIONW CredTargetInfo;
//
// insure all fields NULL.
//
ZeroMemory( &CredTargetInfo, sizeof(CredTargetInfo) );
ZeroMemory( GetChallengeResponse, sizeof(*GetChallengeResponse) );
GetChallengeResponseSize = sizeof(*GetChallengeResponse);
GetChallengeResponse->MessageType = MsV1_0Lm20GetChallengeResponse;
GetChallengeResponse->ParameterControl = 0;
if( Context->Credential )
{
PUNICODE_STRING TmpDomainName = &(Context->Credential->DomainName);
PUNICODE_STRING TmpUserName = &(Context->Credential->UserName);
PUNICODE_STRING TmpPassword = &(Context->Credential->Password);
if( (TmpDomainName->Buffer != NULL) ||
(TmpUserName->Buffer != NULL) ||
(TmpPassword->Buffer != NULL)
)
{
UseSuppliedCreds = TRUE;
}
}
//
// if caller specifically asked for non nt session key, give it to them
//
if ( (ChallengeMessage->NegotiateFlags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY ) ||
(Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY))
{
GetChallengeResponse->ParameterControl |= RETURN_NON_NT_USER_SESSION_KEY;
}
GetChallengeResponse->ParameterControl |= GCR_NTLM3_PARMS;
//
// if TargetInfo present, use it, otherwise construct it from target name
//
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
{
if ( InputTokenSize < sizeof(CHALLENGE_MESSAGE) ) {
SspPrint(( SSP_CRITICAL,
"SspHandleChallengeMessage: "
"ChallengeMessage size wrong when target info flag on %ld\n",
InputTokenSize ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// validate and relocate the target info
//
if (!SspConvertRelativeToAbsolute (
ChallengeMessage,
InputTokenSize,
&ChallengeMessage->TargetInfo,
(PSTRING)&TargetInfo,
DoUnicode,
TRUE // NULL target info OK
))
{
SspPrint(( SSP_CRITICAL,
"SspHandleChallengeMessage: "
"ChallengeMessage.TargetInfo size wrong %ld\n",
InputTokenSize ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Calculate mashal data size for the target info case
//
if( UseSuppliedCreds )
{
MarshalBytes =
TargetInfo.Length +
Context->DomainName.Length +
Context->UserName.Length +
Context->Password.Length +
(4*sizeof(WCHAR));
} else {
MarshalBytes =
TargetInfo.Length +
(DNLEN * sizeof(WCHAR)) +
(UNLEN * sizeof(WCHAR)) +
(PWLEN * sizeof(WCHAR)) +
(4*sizeof(WCHAR));
}
//
// Sets a "reasonable" upper limit on the token size
// to avoid unbounded stack allocations. The limit is
// NTLMSSP_MAX_MESSAGE_SIZE*4 for historical reasons.
//
if((NTLMSSP_MAX_MESSAGE_SIZE*4)<MarshalBytes){
SspPrint(( SSP_CRITICAL,
"SspHandleChallengeMessage: "
"ChallengeMessage.TargetInfo size wrong %ld\n",
InputTokenSize ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Allocate buffer for GetChallengeResponse + marshalled data
//
SafeAllocaAllocate(GetChallengeResponse, MarshalBytes +
sizeof(*GetChallengeResponse));
if(!GetChallengeResponse){
SecStatus = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Copy in the MSV1_0_GETCHALLENRESP_REQUEST structure so far
//
*GetChallengeResponse = TempChallengeResponse;
MarshalPtr = (PCHAR)(GetChallengeResponse+1);
// TargetInfo now contains the server's netbios domain name
//
// MSV needs the server name to be 'in' the passed in buffer.
//
SspContextCopyStringAbsolute(
GetChallengeResponse,
(PSTRING)&GetChallengeResponse->ServerName,
(PSTRING)&TargetInfo,
&MarshalPtr
);
GetChallengeResponseSize += GetChallengeResponse->ServerName.Length;
//
// tell GCR that its an AV list.
//
GetChallengeResponse->ParameterControl |= GCR_TARGET_INFO;
// get various target names
if( !UseSuppliedCreds )
{
ULONG AvFlags = 0;
//
// Uplevel -- get the info from the comprehensive TARGET_INFO
//
//
Status = MsvpAvlToString(
&TargetInfo,
MsvAvNbDomainName,
&szCredTargetDomain
);
if(!NT_SUCCESS(Status))
{
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY );
goto Cleanup;
}
Status = MsvpAvlToString(
&TargetInfo,
MsvAvNbComputerName,
&szCredTargetServer
);
if(!NT_SUCCESS(Status))
{
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY );
goto Cleanup;
}
Status = MsvpAvlToString(
&TargetInfo,
MsvAvDnsDomainName,
&szCredTargetDnsDomain
);
if(!NT_SUCCESS(Status))
{
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY );
goto Cleanup;
}
Status = MsvpAvlToString(
&TargetInfo,
MsvAvDnsComputerName,
&szCredTargetDnsServer
);
if(!NT_SUCCESS(Status))
{
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY );
goto Cleanup;
}
Status = MsvpAvlToString(
&TargetInfo,
MsvAvDnsTreeName,
&szCredTargetDnsTree
);
if(!NT_SUCCESS(Status))
{
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY );
goto Cleanup;
}
if ( TargetServerName && TargetServerName->Length )
{
//
// check TargetServerName against szTargetServer. If they don't match, we have a DFS share.
// Add Pre-DFS ServerName
//
LPWSTR szTargetServerName = TargetServerName->Buffer;
DWORD cchTarget = TargetServerName->Length / sizeof(WCHAR);
DWORD IndexSlash;
for (IndexSlash = 0 ; IndexSlash < cchTarget; IndexSlash++)
{
if(TargetServerName->Buffer[IndexSlash] == L'/')
{
cchTarget -= IndexSlash;
szTargetServerName = &TargetServerName->Buffer[ IndexSlash+1 ];
break;
}
}
szCredTargetPreDFSServer = (LPWSTR)NtLmAllocatePrivateHeap( (cchTarget+1) * sizeof(WCHAR) );
if( szCredTargetPreDFSServer == NULL )
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
CopyMemory( szCredTargetPreDFSServer, szTargetServerName, cchTarget*sizeof(WCHAR) );
szCredTargetPreDFSServer[ cchTarget ] = L'\0';
CredTargetInfo.TargetName = szCredTargetPreDFSServer;
}
//
// see if server enabled the funky guest bit (tm)
//
Status = MsvpAvlToFlag(
&TargetInfo,
MsvAvFlags,
&AvFlags
);
if( Status == STATUS_SUCCESS )
{
if( AvFlags & MSV1_0_AV_FLAG_FORCE_GUEST )
{
CredTargetInfo.Flags |= CRED_TI_ONLY_PASSWORD_REQUIRED;
}
}
}
} else {
BOOLEAN DefectiveTarget = FALSE;
// downlevel - first call may have been handled by redir
//
// validate and relocate the target name
//
if (!SspConvertRelativeToAbsolute (
ChallengeMessage,
InputTokenSize,
&ChallengeMessage->TargetName,
(PSTRING)&TargetName,
DoUnicode,
TRUE // NULL targetname OK
))
{
SspPrint(( SSP_CRITICAL,
"SspHandleChallengeMessage: "
"ChallengeMessage.TargetName size wrong %ld\n",
InputTokenSize ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// certain flavors of Unix servers set UNICODE and OEM flags,
// but supply an OEM buffer. Try to resolve that here.
//
if ( (DoUnicode) &&
(ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_OEM)
)
{
if( IsTextUnicode( TargetName.Buffer, TargetName.Length, NULL ) == 0 )
{
DefectiveTarget = TRUE;
}
}
//
// convert TargetName to Unicode if needed
//
if ( !DoUnicode || DefectiveTarget )
{
UNICODE_STRING TempString;
Status = RtlOemStringToUnicodeString(
&TempString,
(PSTRING)&TargetName,
TRUE);
if ( !NT_SUCCESS(Status) ) {
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY );
goto Cleanup;
}
TargetName = TempString;
if( DefectiveTarget )
{
//
// save it so we can free it later.
//
DefectiveTargetName = TargetName;
}
}
if ( !UseSuppliedCreds )
{
// ChallengeMessage->TargetName will be the server's netbios domain name
if ( TargetName.Buffer && TargetName.Length )
{
LPWSTR szTmpTargetName;
szTmpTargetName = (PWSTR)NtLmAllocatePrivateHeap( TargetName.Length + sizeof(WCHAR) );
if( szTmpTargetName == NULL )
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
RtlCopyMemory( szTmpTargetName, TargetName.Buffer, TargetName.Length );
szTmpTargetName[ TargetName.Length/sizeof(WCHAR) ] = L'\0';
if( ChallengeMessage->NegotiateFlags & NTLMSSP_TARGET_TYPE_SERVER )
{
szCredTargetServer = szTmpTargetName;
} else if( ChallengeMessage->NegotiateFlags & NTLMSSP_TARGET_TYPE_DOMAIN )
{
szCredTargetDomain = szTmpTargetName;
}
// TODO: what if TARGET_TYPE not specified, or TARGET_TYPE_SHARE ?
}
if ( TargetServerName && TargetServerName->Length )
{
//
// check TargetServerName against szTargetServer. If they don't match, we have a DFS share.
// Add Pre-DFS ServerName
//
LPWSTR szTargetServerName = TargetServerName->Buffer;
DWORD cchTarget = TargetServerName->Length / sizeof(WCHAR);
DWORD IndexSlash;
for (IndexSlash = 0 ; IndexSlash < cchTarget; IndexSlash++)
{
if(TargetServerName->Buffer[IndexSlash] == L'/')
{
cchTarget -= IndexSlash;
szTargetServerName = &TargetServerName->Buffer[ IndexSlash+1 ];
break;
}
}
szCredTargetPreDFSServer = (LPWSTR)NtLmAllocatePrivateHeap( (cchTarget+1) * sizeof(WCHAR) );
if( szCredTargetPreDFSServer == NULL )
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
CopyMemory( szCredTargetPreDFSServer, szTargetServerName, cchTarget*sizeof(WCHAR) );
szCredTargetPreDFSServer[ cchTarget ] = L'\0';
CredTargetInfo.TargetName = szCredTargetPreDFSServer;
}
if( fShareLevel )
{
CredTargetInfo.Flags |= CRED_TI_ONLY_PASSWORD_REQUIRED;
}
}
//
// Calculate mashal data size for the target name case
//
if( UseSuppliedCreds )
{
MarshalBytes =
TargetName.Length +
Context->DomainName.Length +
Context->UserName.Length +
Context->Password.Length +
(4*sizeof(WCHAR));
} else {
MarshalBytes =
TargetName.Length +
(DNLEN * sizeof(WCHAR)) +
(UNLEN * sizeof(WCHAR)) +
(PWLEN * sizeof(WCHAR)) +
(4*sizeof(WCHAR));
}
//
// Set a "reasonable" upper limit on the token size
// to avoid unbounded stack allocations. The limit is
// NTLMSSP_MAX_MESSAGE_SIZE*4 for historical reasons.
//
if((NTLMSSP_MAX_MESSAGE_SIZE*4)<MarshalBytes){
SspPrint(( SSP_CRITICAL,
"SspHandleChallengeMessage: "
"ChallengeMessage size wrong \n"));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Allocate buffer for GetChallengeResponse + marshalled data
//
SafeAllocaAllocate(GetChallengeResponse, MarshalBytes +
sizeof(*GetChallengeResponse));
if(!GetChallengeResponse){
SecStatus = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Copy in the MSV1_0_GETCHALLENRESP_REQUEST structure so far
//
*GetChallengeResponse = TempChallengeResponse;
MarshalPtr = (PCHAR)(GetChallengeResponse+1);
//
// MSV needs the server name to be 'in' the passed in buffer.
//
SspContextCopyStringAbsolute(
GetChallengeResponse,
(PSTRING)&GetChallengeResponse->ServerName,
(PSTRING)&TargetName,
&MarshalPtr
);
GetChallengeResponseSize += GetChallengeResponse->ServerName.Length;
}
if ( !UseSuppliedCreds )
{
ULONG CredTypes = CRED_TYPE_DOMAIN_PASSWORD;
CredTargetInfo.NetbiosDomainName = szCredTargetDomain;
CredTargetInfo.NetbiosServerName = szCredTargetServer;
CredTargetInfo.DnsDomainName = szCredTargetDnsDomain;
CredTargetInfo.DnsServerName = szCredTargetDnsServer;
CredTargetInfo.DnsTreeName = szCredTargetDnsTree;
CredTargetInfo.PackageName = NTLMSP_NAME;
CredTargetInfo.CredTypeCount = 1;
CredTargetInfo.CredTypes = &CredTypes;
//
// if marshalled TargetInfo was supplied, we prefer those fields.
//
if( Context->TargetInfo )
{
CredTargetInfo.TargetName = Context->TargetInfo->TargetName;
CredTargetInfo.NetbiosServerName = Context->TargetInfo->NetbiosServerName;
CredTargetInfo.DnsServerName = Context->TargetInfo->DnsServerName;
CredTargetInfo.NetbiosDomainName = Context->TargetInfo->NetbiosDomainName;
CredTargetInfo.DnsDomainName = Context->TargetInfo->DnsDomainName;
CredTargetInfo.DnsTreeName = Context->TargetInfo->DnsTreeName;
CredTargetInfo.Flags |= Context->TargetInfo->Flags;
}
SecStatus = CopyCredManCredentials (
ClientLogonId,
&CredTargetInfo,
Context,
fShareLevel
);
if( NT_SUCCESS(SecStatus) )
{
fCredmanCredentials = TRUE;
}
if( SecStatus == STATUS_NOT_FOUND )
{
SecStatus = STATUS_SUCCESS;
}
if( !NT_SUCCESS(SecStatus) )
{
goto Cleanup;
}
//
// for kernel callers, stow away a copy of the marshalled target info.
//
if( Context->KernelClient )
{
CredMarshalTargetInfo(
&CredTargetInfo,
(PUSHORT*)&(Context->pbMarshalledTargetInfo),
&Context->cbMarshalledTargetInfo
);
}
}
SspContextCopyStringAbsolute(
GetChallengeResponse,
(PSTRING)&GetChallengeResponse->LogonDomainName,
(PSTRING)&Context->DomainName,
&MarshalPtr
);
GetChallengeResponseSize += GetChallengeResponse->LogonDomainName.Length;
SspContextCopyStringAbsolute(
GetChallengeResponse,
(PSTRING)&GetChallengeResponse->UserName,
(PSTRING)&Context->UserName,
&MarshalPtr
);
GetChallengeResponseSize += GetChallengeResponse->UserName.Length;
//
// Check for null session. This is the case if the caller supplies
// an empty username, domainname, and password.
//
//
// duplicate the hidden password string, then reveal it into
// new buffer. This avoids thread race conditions during hide/reveal
// and also allows us to avoid re-hiding the material.
// TODO: add flag that indicates to LSA that provided password is encrypted.
//
SecStatus = NtLmDuplicatePassword( &RevealedPassword, &Context->Password );
if(!NT_SUCCESS( SecStatus ) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmDuplicatePassword returned %d\n",Status));
goto Cleanup;
}
SspRevealPassword(&RevealedPassword);
if (((Context->ContextFlags & ISC_RET_NULL_SESSION) != 0) ||
(((Context->DomainName.Length == 0) && (Context->DomainName.Buffer != NULL)) &&
((Context->UserName.Length == 0) && (Context->UserName.Buffer != NULL)) &&
((Context->Password.Length == 0) && (Context->Password.Buffer != NULL)))) {
#define NULL_SESSION_REQUESTED RETURN_RESERVED_PARAMETER
SspPrint(( SSP_WARNING, "SsprHandleChallengeMessage: null session context\n" ));
GetChallengeResponse->ParameterControl |= NULL_SESSION_REQUESTED |
USE_PRIMARY_PASSWORD;
} else {
//
// We aren't doing a null session intentionally. MSV may choose
// to do a null session if we have no credentials available.
//
if ( Context->DomainName.Buffer == NULL )
{
BOOLEAN FoundAt = FALSE;
//
// if it's a UPN, don't fill in the domain field.
//
if( Context->UserName.Buffer != NULL )
{
ULONG i;
for(i = 0 ; i < (Context->UserName.Length / sizeof(WCHAR)) ; i++)
{
if( Context->UserName.Buffer[i] == '@' )
{
FoundAt = TRUE;
break;
}
}
}
if( !FoundAt )
{
GetChallengeResponse->ParameterControl |=
RETURN_PRIMARY_LOGON_DOMAINNAME;
}
}
if ( Context->UserName.Buffer == NULL )
{
GetChallengeResponse->ParameterControl |= RETURN_PRIMARY_USERNAME;
}
//
// The password may be a zero length password
//
GetChallengeResponse->Password = RevealedPassword;
if ( Context->Password.Buffer == NULL ) {
GetChallengeResponse->ParameterControl |= USE_PRIMARY_PASSWORD;
} else {
//
// MSV needs the password to be 'in' the passed in buffer.
//
RtlCopyMemory(MarshalPtr,
GetChallengeResponse->Password.Buffer,
GetChallengeResponse->Password.Length);
GetChallengeResponse->Password.Buffer =
(LPWSTR)(MarshalPtr);
GetChallengeResponseSize += GetChallengeResponse->Password.Length +
sizeof(WCHAR);
}
}
//
// scrub the cleartext password now to avoid pagefile exposure
// during lengthy processing.
//
if( RevealedPassword.Buffer != NULL ) {
ZeroMemory( RevealedPassword.Buffer, RevealedPassword.Length );
NtLmFreePrivateHeap( RevealedPassword.Buffer );
RevealedPassword.Buffer = NULL;
}
GetChallengeResponse->LogonId = *ClientLogonId;
RtlCopyMemory( &GetChallengeResponse->ChallengeToClient,
ChallengeMessage->Challenge,
MSV1_0_CHALLENGE_LENGTH );
//
// if NTLM2 negotiated, then ask MSV1_0 to mix my challenge with the server's...
//
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
{
GetChallengeResponse->ParameterControl |= GENERATE_CLIENT_CHALLENGE;
} else {
//
// if it's share level, and:
// 1. User only supplied a password, or
// 2. Credman returned the creds
// allow downgrade to NTLMv1 from NTLMv2.
//
if( fShareLevel )
{
if( (GetChallengeResponse->UserName.Length == 0) &&
(GetChallengeResponse->LogonDomainName.Length == 0) &&
(GetChallengeResponse->Password.Buffer != NULL)
)
{
GetChallengeResponse->ParameterControl |= GCR_ALLOW_NTLM;
}
if( fCredmanCredentials )
{
GetChallengeResponse->ParameterControl |= GCR_ALLOW_NTLM;
}
}
}
//
// Get the DomainName, UserName, and ChallengeResponse from the MSV
//
Status = LsaApCallPackage(
(PLSA_CLIENT_REQUEST)(-1),
GetChallengeResponse,
GetChallengeResponse,
GetChallengeResponseSize,
(PVOID *)&ChallengeResponseMessage,
&ChallengeResponseSize,
&ProtocolStatus );
if ( !NT_SUCCESS(Status) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: "
"ChallengeMessage LsaCall to get ChallengeResponse returns 0x%lx\n",
Status ));
SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_CREDENTIALS);
goto Cleanup;
}
if ( !NT_SUCCESS(ProtocolStatus) ) {
Status = ProtocolStatus;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: ChallengeMessage LsaCall "
"to get ChallengeResponse returns ProtocolStatus 0x%lx\n",
Status ));
SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_CREDENTIALS);
goto Cleanup;
}
//
// Check to see if we are doing a null session
//
if ((ChallengeResponseMessage->CaseSensitiveChallengeResponse.Length == 0) &&
(ChallengeResponseMessage->CaseInsensitiveChallengeResponse.Length == 1)) {
SspPrint(( SSP_WARNING, "SsprHandleChallengeMessage: null session\n" ));
*ContextAttributes |= ISC_RET_NULL_SESSION;
Context->ContextFlags |= ISC_RET_NULL_SESSION;
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
} else {
//
// Normalize things by copying the default domain name and user name
// into the ChallengeResponseMessage structure.
//
if ( Context->DomainName.Buffer != NULL ) {
ChallengeResponseMessage->LogonDomainName = Context->DomainName;
}
if ( Context->UserName.Buffer != NULL ) {
ChallengeResponseMessage->UserName = Context->UserName;
}
}
//
// Convert the domainname/user name to the right character set.
//
if ( DoUnicode ) {
DomainName = *(PSTRING)&ChallengeResponseMessage->LogonDomainName;
UserName = *(PSTRING)&ChallengeResponseMessage->UserName;
Workstation = *(PSTRING)&NtLmGlobalUnicodeComputerNameString;
} else {
Status = RtlUpcaseUnicodeStringToOemString(
&DomainName,
&ChallengeResponseMessage->LogonDomainName,
TRUE);
if ( !NT_SUCCESS(Status) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: RtlUpcaseUnicodeToOemString (DomainName) returned 0x%lx.\n", Status));
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY);
goto Cleanup;
}
Status = RtlUpcaseUnicodeStringToOemString(
&UserName,
&ChallengeResponseMessage->UserName,
TRUE);
if ( !NT_SUCCESS(Status) ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: RtlUpcaseUnicodeToOemString (UserName) returned 0x%lx.\n", Status));
SecStatus = SspNtStatusToSecStatus( Status,
SEC_E_INSUFFICIENT_MEMORY);
goto Cleanup;
}
Workstation = NtLmGlobalOemComputerNameString;
}
//
// Save the ChallengeResponses
//
LmChallengeResponse =
ChallengeResponseMessage->CaseInsensitiveChallengeResponse;
NtChallengeResponse =
ChallengeResponseMessage->CaseSensitiveChallengeResponse;
//
// prepare to send encrypted randomly generated session key
//
DatagramSessionKey.Buffer = (CHAR*)DatagramKey;
DatagramSessionKey.Length =
DatagramSessionKey.MaximumLength = 0;
//
// Generate the session key, or encrypt the previosly generated random one,
// from various bits of info. Fill in session key if needed.
//
SecStatus = SsprMakeSessionKey(
Context,
&LmChallengeResponse,
ChallengeResponseMessage->UserSessionKey,
ChallengeResponseMessage->LanmanSessionKey,
&DatagramSessionKey
);
if (SecStatus != SEC_E_OK)
{
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: SsprMakeSessionKey\n"));
goto Cleanup;
}
}
//
// If the caller specified SEQUENCE_DETECT or REPLAY_DETECT,
// that means they want to use the MakeSignature/VerifySignature
// calls. Add this to the returned attributes and the context
// negotiate flags.
//
if ((Context->NegotiateFlags &
ChallengeMessage->NegotiateFlags &
NTLMSSP_NEGOTIATE_SIGN) ||
(ContextReqFlags & ISC_REQ_REPLAY_DETECT)) {
Context->ContextFlags |= ISC_RET_REPLAY_DETECT;
*ContextAttributes |= ISC_RET_REPLAY_DETECT;
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
}
if ((Context->NegotiateFlags &
ChallengeMessage->NegotiateFlags &
NTLMSSP_NEGOTIATE_SIGN) ||
(ContextReqFlags & ISC_REQ_SEQUENCE_DETECT)) {
Context->ContextFlags |= ISC_RET_SEQUENCE_DETECT;
*ContextAttributes |= ISC_RET_SEQUENCE_DETECT;
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
}
if ((Context->NegotiateFlags &
ChallengeMessage->NegotiateFlags &
NTLMSSP_NEGOTIATE_SIGN) ||
(ContextReqFlags & ISC_REQ_INTEGRITY)) {
Context->ContextFlags |= ISC_RET_INTEGRITY;
*ContextAttributes |= ISC_RET_INTEGRITY;
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
}
if ((Context->NegotiateFlags &
ChallengeMessage->NegotiateFlags &
NTLMSSP_NEGOTIATE_SEAL) ||
(ContextReqFlags & ISC_REQ_CONFIDENTIALITY)) {
if (NtLmGlobalEncryptionEnabled) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
Context->ContextFlags |= ISC_REQ_CONFIDENTIALITY;
*ContextAttributes |= ISC_REQ_CONFIDENTIALITY;
} else {
SecStatus = STATUS_NOT_SUPPORTED;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: NtLmGlobalEncryption not enabled.\n"));
goto Cleanup;
}
}
if ((Context->NegotiateFlags &
ChallengeMessage->NegotiateFlags &
NTLMSSP_NEGOTIATE_DATAGRAM) ==
NTLMSSP_NEGOTIATE_DATAGRAM ) {
*ContextAttributes |= ISC_RET_DATAGRAM;
Context->ContextFlags |= ISC_RET_DATAGRAM;
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_DATAGRAM;
}
//
// Slip in the hacky mutual auth override here:
//
if ( ((ContextReqFlags & ISC_REQ_MUTUAL_AUTH) != 0 ) &&
(NtLmGlobalMutualAuthLevel < 2 ) ) {
*ContextAttributes |= ISC_RET_MUTUAL_AUTH ;
if ( NtLmGlobalMutualAuthLevel == 0 )
{
Context->ContextFlags |= ISC_RET_MUTUAL_AUTH ;
}
}
if( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_LOCAL_CALL) &&
(ContextReqFlags & ISC_REQ_DELEGATE)
)
{
//
// for loopback, we can indeed support another hop.
//
*ContextAttributes |= ISC_RET_DELEGATE;
Context->ContextFlags |= ISC_RET_DELEGATE;
}
//
// Allocate an authenticate message
//
AuthenticateMessageSize =
sizeof(*AuthenticateMessage) +
LmChallengeResponse.Length +
NtChallengeResponse.Length +
DomainName.Length +
UserName.Length +
Workstation.Length +
DatagramSessionKey.Length;
if ((ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if ( AuthenticateMessageSize > *OutputTokenSize ) {
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: OutputTokenSize is 0x%lx.\n", *OutputTokenSize));
SecStatus = SEC_E_BUFFER_TOO_SMALL;
goto Cleanup;
}
}
AuthenticateMessage = (PAUTHENTICATE_MESSAGE)
NtLmAllocateLsaHeap(AuthenticateMessageSize );
if ( AuthenticateMessage == NULL ) {
SecStatus = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Error allocating AuthenticateMessage.\n" ));
goto Cleanup;
}
//
// Build the authenticate message
//
strcpy( (char *)AuthenticateMessage->Signature, NTLMSSP_SIGNATURE );
AuthenticateMessage->MessageType = NtLmAuthenticate;
Where = (PCHAR)(AuthenticateMessage+1);
//
// Copy the strings needing 2 byte alignment.
//
SspContextCopyString( AuthenticateMessage,
&AuthenticateMessage->DomainName,
&DomainName,
&Where );
SspContextCopyString( AuthenticateMessage,
&AuthenticateMessage->UserName,
&UserName,
&Where );
SspContextCopyString( AuthenticateMessage,
&AuthenticateMessage->Workstation,
&Workstation,
&Where );
//
// Copy the strings not needing special alignment.
//
SspContextCopyString( AuthenticateMessage,
&AuthenticateMessage->LmChallengeResponse,
&LmChallengeResponse,
&Where );
SspContextCopyString( AuthenticateMessage,
&AuthenticateMessage->NtChallengeResponse,
&NtChallengeResponse,
&Where );
SspContextCopyString( AuthenticateMessage,
&AuthenticateMessage->SessionKey,
&DatagramSessionKey,
&Where );
AuthenticateMessage->NegotiateFlags = Context->NegotiateFlags;
SspPrint(( SSP_NEGOTIATE_FLAGS,
"SsprHandleChallengeMessage: ChallengeFlags: %lx AuthenticateFlags: %lx\n",
ChallengeMessage->NegotiateFlags, AuthenticateMessage->NegotiateFlags ));
//
// Copy the AuthenticateMessage to the caller's address space.
//
if ((ContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
RtlCopyMemory( *OutputToken,
AuthenticateMessage,
AuthenticateMessageSize );
}
else
{
*OutputToken = AuthenticateMessage;
AuthenticateMessage = NULL;
*ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
}
*OutputTokenSize = AuthenticateMessageSize;
// we need to send a second token back for the rdr
if (fCallFromRedir)
{
NtLmInitializeResponse = (PNTLM_INITIALIZE_RESPONSE)
NtLmAllocateLsaHeap(sizeof(NTLM_INITIALIZE_RESPONSE));
if ( NtLmInitializeResponse == NULL ) {
SecStatus = STATUS_NO_MEMORY;
SspPrint(( SSP_CRITICAL,
"SsprHandleChallengeMessage: Error allocating NtLmInitializeResponse.\n" ));
goto Cleanup;
}
RtlCopyMemory(
NtLmInitializeResponse->UserSessionKey,
ChallengeResponseMessage->UserSessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH
);
RtlCopyMemory(
NtLmInitializeResponse->LanmanSessionKey,
ChallengeResponseMessage->LanmanSessionKey,
MSV1_0_LANMAN_SESSION_KEY_LENGTH
);
*SecondOutputToken = NtLmInitializeResponse;
NtLmInitializeResponse = NULL;
*SecondOutputTokenSize = sizeof(NTLM_INITIALIZE_RESPONSE);
}
SspPrint((SSP_API_MORE,"Client session key = %p\n",Context->SessionKey));
//
// Return output parameters to the caller.
//
*ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
SecStatus = STATUS_SUCCESS;
//
// Free and locally used resources.
//
Cleanup:
if( RevealedPassword.Buffer != NULL ) {
ZeroMemory( RevealedPassword.Buffer, RevealedPassword.Length );
NtLmFreePrivateHeap( RevealedPassword.Buffer );
}
if( ServerContext != NULL )
{
SspContextDereferenceContext( ServerContext );
}
if ( Context != NULL ) {
Context->LastStatus = SecStatus;
Context->DownLevel = fCallFromRedir;
//
// Don't allow this context to be used again.
//
if ( NT_SUCCESS(SecStatus) ) {
Context->State = AuthenticateSentState;
} else if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) {
Context->State = PassedToServiceState;
} else {
Context->State = IdleState;
}
RtlCopyMemory(
SessionKey,
Context->SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH );
*NegotiateFlags = Context->NegotiateFlags;
// If we just created the context (because rdr may be talking to
// a pre NT 5.0 server,, we need to dereference it again.
if (fCallFromRedir && !NT_SUCCESS(SecStatus))
{
PSSP_CONTEXT LocalContext;
SspContextReferenceContext( *ContextHandle, TRUE, &LocalContext );
ASSERT(LocalContext != NULL);
if (LocalContext != NULL)
{
SspContextDereferenceContext( LocalContext );
}
}
SspContextDereferenceContext( Context );
}
if(szCredTargetPreDFSServer != NULL)
{
NtLmFreePrivateHeap( szCredTargetPreDFSServer );
}
if(szCredTargetDomain != NULL)
{
NtLmFreePrivateHeap( szCredTargetDomain );
}
if(szCredTargetServer != NULL)
{
NtLmFreePrivateHeap( szCredTargetServer );
}
if(szCredTargetDnsDomain != NULL)
{
NtLmFreePrivateHeap( szCredTargetDnsDomain );
}
if(szCredTargetDnsServer != NULL)
{
NtLmFreePrivateHeap( szCredTargetDnsServer );
}
if(szCredTargetDnsTree != NULL)
{
NtLmFreePrivateHeap( szCredTargetDnsTree );
}
if ( ChallengeMessage != NULL ) {
(VOID) NtLmFreePrivateHeap( ChallengeMessage );
}
if ( AuthenticateMessage != NULL ) {
(VOID) NtLmFreeLsaHeap( AuthenticateMessage );
}
if ( ChallengeResponseMessage != NULL ) {
(VOID) LsaFunctions->FreeLsaHeap( ChallengeResponseMessage );
}
if ( !DoUnicode ) {
RtlFreeUnicodeString( &TargetName );
if ( DomainName.Buffer != NULL) {
RtlFreeOemString( &DomainName );
}
if ( UserName.Buffer != NULL) {
RtlFreeOemString( &UserName );
}
}
RtlFreeUnicodeString( &DefectiveTargetName );
if( ClientTokenHandle )
{
CloseHandle( ClientTokenHandle );
}
if(GetChallengeResponse && (GetChallengeResponse!=&TempChallengeResponse)){
SafeAllocaFree(GetChallengeResponse);
}
SspPrint(( SSP_API_MORE, "Leaving SsprHandleChallengeMessage: 0x%lx\n", SecStatus ));
return SecStatus;
}
NTSTATUS
CredpParseUserName(
IN OUT LPWSTR ParseName,
OUT LPWSTR* pUserName,
OUT LPWSTR* pDomainName
)
/*++
Routine Description:
This routine separates a passed in user name into domain and username. A user name must have one
of the following two syntaxes:
<DomainName>\<UserName>
<UserName>@<DnsDomainName>
The name is considered to have the first syntax if the string contains an \.
A string containing a @ is ambiguous since <UserName> may contain an @.
For the second syntax, the last @ in the string is used since <UserName> may
contain an @ but <DnsDomainName> cannot.
Arguments:
ParseName - Name of user to validate - will be modified
pUserName - Returned pointing to canonical name inside of ParseName
pDomainName - Returned pointing to domain name inside of ParseName
Return Values:
The following status codes may be returned:
STATUS_INVALID_ACCOUNT_NAME - The user name is not valid.
--*/
{
LPWSTR SlashPointer;
*pUserName = NULL;
*pDomainName = NULL;
//
// NULL is invalid
//
if ( ParseName == NULL ) {
return STATUS_INVALID_ACCOUNT_NAME;
}
//
// Classify the input account name.
//
// The name is considered to be <DomainName>\<UserName> if the string
// contains an \.
//
SlashPointer = wcsrchr( ParseName, L'\\' );
if ( SlashPointer != NULL ) {
//
// point the output strings
//
*pDomainName = ParseName;
//
// Skip the backslash
//
*SlashPointer = L'\0';
SlashPointer ++;
*pUserName = SlashPointer;
} else {
//
// it's a UPN.
// leave it intact in the UserName field.
// set the DomainName to empty string, so the rest of the logon code
// avoids filling in the default.
//
*pUserName = ParseName;
*pDomainName = L"";
}
return STATUS_SUCCESS;
}
NTSTATUS
CopyCredManCredentials(
IN PLUID LogonId,
CREDENTIAL_TARGET_INFORMATIONW* pTargetInfo,
IN OUT PSSP_CONTEXT Context,
IN BOOLEAN fShareLevel
)
/*++
Routine Description:
Look for a keyring credential entry for the specified domain, and copy to Context handle if found
Arguments:
LogonId -- LogonId of the calling process.
pTargetInfo -- Information on target to search for creds.
Context - Points to the ContextHandle of the Context
to be referenced.
Return Value:
STATUS_SUCCESS -- All OK
STATUS_NOT_FOUND - Credential couldn't be found.
All others are real failures and should be returned to the caller.
--*/
{
NTSTATUS Status;
PENCRYPTED_CREDENTIALW *EncryptedCredentials = NULL;
PCREDENTIALW *Credentials = NULL;
ULONG CredentialCount;
WCHAR* UserName = NULL;
WCHAR* DomainName = NULL;
ULONG CredIndex;
if (!Context) // validate context only after call to Lookup
{
return STATUS_NOT_FOUND;
}
Status = LsaFunctions->CrediReadDomainCredentials(
LogonId,
CREDP_FLAGS_IN_PROCESS, // Allow password to be returned
pTargetInfo,
0, // no flags
&CredentialCount,
&EncryptedCredentials );
Credentials = (PCREDENTIALW *)EncryptedCredentials;
if(!NT_SUCCESS(Status))
{
//
// Ideally, only STATUS_NO_SUCH_LOGON_SESSION should be converted to
// STATUS_NOT_FOUND. However, swallowing all failures and asserting
// these specific two works around a bug in CrediReadDomainCredentials
// which returns invalid parameter if the target is a user account name.
// Eventually, CrediReadDomainCredentials should return a more appropriate
// error in this case.
//
SspPrint(( SSP_API_MORE, "CopyCredManCredentials: CrediReadDomainCredentials returned %x\n", Status ));
ASSERT(
(Status == STATUS_NO_SUCH_LOGON_SESSION)||
(Status == STATUS_INVALID_PARAMETER)||
(Status == STATUS_NOT_FOUND)
);
return STATUS_NOT_FOUND;
}
//
// Loop through the list of credentials
//
for ( CredIndex=0; CredIndex<CredentialCount; CredIndex++ ) {
UNICODE_STRING TempString;
//
// NTLM only supports password credentials
//
if ( Credentials[CredIndex]->Type != CRED_TYPE_DOMAIN_PASSWORD ) {
continue;
}
if ( Credentials[CredIndex]->Flags & CRED_FLAGS_PROMPT_NOW ) {
Status = SEC_E_LOGON_DENIED;
goto Cleanup;
}
//
// Sanity check the credential
//
if ( Credentials[CredIndex]->UserName == NULL ) {
Status = STATUS_NOT_FOUND;
goto Cleanup;
}
//
// For Share level connects, don't allow matching against * creds.
//
if( fShareLevel )
{
if( (Credentials[CredIndex]->TargetName) &&
(wcschr( Credentials[CredIndex]->TargetName, L'*' ) != NULL) )
{
continue;
}
}
//
// Convert the UserName to domain name and user name
//
Status = CredpParseUserName ( Credentials[CredIndex]->UserName, &UserName, &DomainName );
if(!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Free the existing domain name and add the new one
//
if (Context->DomainName.Buffer) {
NtLmFreePrivateHeap (Context->DomainName.Buffer);
Context->DomainName.Buffer = NULL;
Context->DomainName.Length = 0;
}
if( DomainName )
{
RtlInitUnicodeString( &TempString, DomainName );
Status = NtLmDuplicateUnicodeString(&Context->DomainName, &TempString);
if ( !NT_SUCCESS( Status ) )
{
goto Cleanup;
}
}
//
// Free the existing user name and add the new one
//
RtlInitUnicodeString( &TempString, UserName );
if (Context->UserName.Buffer) {
NtLmFreePrivateHeap (Context->UserName.Buffer);
Context->UserName.Buffer = NULL;
}
Status = NtLmDuplicateUnicodeString(&Context->UserName, &TempString);
if ( !NT_SUCCESS( Status ) )
{
goto Cleanup;
}
//
// Free the existing password and add the new one
//
TempString.Buffer = (LPWSTR)Credentials[CredIndex]->CredentialBlob;
TempString.MaximumLength = (USHORT) Credentials[CredIndex]->CredentialBlobSize;
TempString.Length = (USHORT) EncryptedCredentials[CredIndex]->ClearCredentialBlobSize;
if (Context->Password.Buffer) {
NtLmFreePrivateHeap (Context->Password.Buffer);
Context->Password.Buffer = NULL;
}
// zero length password must be treated as blank or NTLM will assume it should use the
// password of the currently logged in user.
if ( TempString.Length == 0 )
{
TempString.Buffer = L"";
}
Status = NtLmDuplicatePassword(&Context->Password, &TempString);
if ( !NT_SUCCESS( Status ) )
{
goto Cleanup;
}
goto Cleanup;
}
Status = STATUS_NOT_FOUND;
Cleanup:
//
// Free the returned credentials
//
LsaFunctions->CrediFreeCredentials(
CredentialCount,
EncryptedCredentials );
return Status;
}
NTSTATUS
CredpExtractMarshalledTargetInfo(
IN PUNICODE_STRING TargetServerName,
OUT CREDENTIAL_TARGET_INFORMATIONW **pTargetInfo
)
{
PWSTR Candidate;
ULONG CandidateSize;
CREDENTIAL_TARGET_INFORMATIONW *OldTargetInfo = NULL;
NTSTATUS Status = STATUS_SUCCESS;
//
// LSA will set Length to include only the non-marshalled portion,
// with MaximumLength trailing data to include marshalled portion.
//
if( (TargetServerName == NULL) ||
(TargetServerName->Buffer == NULL) ||
(TargetServerName->Length >= TargetServerName->MaximumLength) ||
((TargetServerName->MaximumLength - TargetServerName->Length) <
(sizeof( CREDENTIAL_TARGET_INFORMATIONW )/(sizeof(ULONG_PTR)/2)) )
)
{
return STATUS_SUCCESS;
}
RtlCopyMemory(
&CandidateSize,
(PBYTE)TargetServerName->Buffer + TargetServerName->MaximumLength - sizeof(ULONG),
sizeof( CandidateSize )
);
if( CandidateSize >= TargetServerName->MaximumLength )
{
return STATUS_SUCCESS;
}
Candidate = (PWSTR)(
(PBYTE)TargetServerName->Buffer + TargetServerName->MaximumLength - CandidateSize
);
OldTargetInfo = *pTargetInfo;
Status = CredUnmarshalTargetInfo (
Candidate,
CandidateSize,
pTargetInfo
);
if( !NT_SUCCESS(Status) )
{
if( Status == STATUS_INVALID_PARAMETER )
{
Status = STATUS_SUCCESS;
}
}
if( OldTargetInfo != NULL )
{
LocalFree( OldTargetInfo );
}
return Status ;
}
NTSTATUS
CredpProcessUserNameCredential(
IN PUNICODE_STRING MarshalledUserName,
OUT PUNICODE_STRING UserName,
OUT PUNICODE_STRING DomainName,
OUT PUNICODE_STRING Password
)
{
WCHAR FastUserName[ UNLEN+1 ];
LPWSTR SlowUserName = NULL;
LPWSTR TempUserName;
CRED_MARSHAL_TYPE CredMarshalType;
PUSERNAME_TARGET_CREDENTIAL_INFO pCredentialUserName = NULL;
CREDENTIAL_TARGET_INFORMATIONW TargetInfo;
ULONG CredTypes;
SECPKG_CLIENT_INFO ClientInfo;
SSP_CONTEXT SspContext;
NTSTATUS Status = STATUS_NOT_FOUND;
ZeroMemory( &SspContext, sizeof(SspContext) );
if( (MarshalledUserName->Length+sizeof(WCHAR)) <= sizeof(FastUserName) )
{
TempUserName = FastUserName;
} else {
SlowUserName = (LPWSTR)NtLmAllocatePrivateHeap( MarshalledUserName->Length + sizeof(WCHAR) );
if( SlowUserName == NULL )
{
return STATUS_INSUFFICIENT_RESOURCES;
}
TempUserName = SlowUserName;
}
//
// copy the input to a NULL terminated string, then attempt to unmarshal it.
//
RtlCopyMemory( TempUserName,
MarshalledUserName->Buffer,
MarshalledUserName->Length
);
TempUserName[ MarshalledUserName->Length / sizeof(WCHAR) ] = L'\0';
if(!CredUnmarshalCredentialW(
TempUserName,
&CredMarshalType,
(VOID**)&pCredentialUserName
))
{
goto Cleanup;
}
if( (CredMarshalType != UsernameTargetCredential) )
{
goto Cleanup;
}
//
// now query credential manager for a match.
//
Status = LsaFunctions->GetClientInfo(&ClientInfo);
if(!NT_SUCCESS(Status))
{
goto Cleanup;
}
ZeroMemory( &TargetInfo, sizeof(TargetInfo) );
CredTypes = CRED_TYPE_DOMAIN_PASSWORD;
TargetInfo.Flags = CRED_TI_USERNAME_TARGET;
TargetInfo.TargetName = pCredentialUserName->UserName;
TargetInfo.PackageName = NTLMSP_NAME;
TargetInfo.CredTypeCount = 1;
TargetInfo.CredTypes = &CredTypes;
Status = CopyCredManCredentials(
&ClientInfo.LogonId,
&TargetInfo,
&SspContext,
FALSE
);
if(!NT_SUCCESS(Status))
{
goto Cleanup;
}
*UserName = SspContext.UserName;
*DomainName = SspContext.DomainName;
*Password = SspContext.Password;
SspRevealPassword( Password );
Status = STATUS_SUCCESS;
Cleanup:
if(!NT_SUCCESS(Status))
{
NtLmFreePrivateHeap( SspContext.UserName.Buffer );
NtLmFreePrivateHeap( SspContext.DomainName.Buffer );
NtLmFreePrivateHeap( SspContext.Password.Buffer );
}
if( SlowUserName )
{
NtLmFreePrivateHeap( SlowUserName );
}
if( pCredentialUserName != NULL )
{
CredFree( pCredentialUserName );
}
return Status;
}
#if 0
//+-------------------------------------------------------------------------
//
// Function: SpQueryLsaModeContextAttributes
//
// Synopsis: Querys attributes of the specified context
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
ULONG SavedUseValidated = 0xFF;
WCHAR SavedCredentialName[1024] = L"SaveMe";
ULONG SavedCredentialType = 0x22;
ULONG SavedCredTypes = CRED_TYPE_DOMAIN_PASSWORD;
CREDENTIAL_TARGET_INFORMATIONW SavedTargetInfo = {
L"ntdsdc9",
L"NTDSDC9",
L"NTDSDC9.ntdev.microsoft.com",
L"NTDEV",
L"ntdev.microsoft.com",
L"ntdev.microsoft.com",
L"NTLM",
0,
1,
&SavedCredTypes
};
NTSTATUS NTAPI
SpQueryLsaModeContextAttributes(
IN LSA_SEC_HANDLE ContextHandle,
IN ULONG ContextAttribute,
IN OUT PVOID Buffer
)
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD WinStatus;
PSSP_CONTEXT Context = NULL;
SecPkgContext_CredentialNameW CredentialNameInfo;
ULONG CredentialNameSize;
LPWSTR UserCredentialName;
PCREDENTIAL_TARGET_INFORMATIONW TempTargetInfo = NULL;
ULONG TempTargetInfoSize;
PCREDENTIAL_TARGET_INFORMATIONW UserTargetInfo;
SecPkgContext_TargetInformationW TargetInfo;
SspPrint((SSP_API, "Entering SpQueryLsaModeContextAttributes for ctxt:0x%x, Attr:0x%x\n", ContextHandle, ContextAttribute));
//
// Find the currently existing context.
//
Status = SspContextReferenceContext( ContextHandle, FALSE, &Context );
if ( !NT_SUCCESS(Status) )
{
SspPrint(( SSP_CRITICAL,
"SpQueryLsaModeContextAttributes: invalid context handle.\n" ));
goto Cleanup;
}
//
// Return the appropriate information
//
switch(ContextAttribute) {
case SECPKG_ATTR_CREDENTIAL_NAME:
CredentialNameSize = (wcslen(SavedCredentialName) + 1) * sizeof(WCHAR);
Status = LsaFunctions->AllocateClientBuffer(
NULL,
CredentialNameSize,
(PVOID *) &UserCredentialName );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
// Copy the name to the user's address space
Status = LsaFunctions->CopyToClientBuffer(
NULL,
CredentialNameSize,
UserCredentialName,
SavedCredentialName );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
// Copy the struct itself to the user's address space
CredentialNameInfo.CredentialType = SavedCredentialType;
CredentialNameInfo.sCredentialName = UserCredentialName;
Status = LsaFunctions->CopyToClientBuffer(
NULL,
sizeof(CredentialNameInfo),
Buffer,
&CredentialNameInfo );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
break;
case SECPKG_ATTR_TARGET_INFORMATION:
//
// Marshall the target info into a single buffer.
//
WinStatus = CredpConvertTargetInfo ( DoWtoW,
&SavedTargetInfo,
&TempTargetInfo,
&TempTargetInfoSize );
if ( WinStatus != NO_ERROR ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Allocate a buffer the same size in the client's address space.
//
Status = LsaFunctions->AllocateClientBuffer(
NULL,
TempTargetInfoSize,
(PVOID *) &UserTargetInfo );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
//
// Relocate all pointers to be user-buffer-specific
// YUCK!!
//
TempTargetInfo->TargetName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->TargetName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->NetbiosServerName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->NetbiosServerName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->DnsServerName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->DnsServerName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->NetbiosDomainName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->NetbiosDomainName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->DnsDomainName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->DnsDomainName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->DnsTreeName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->DnsTreeName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->PackageName = (LPWSTR)( ((LPBYTE)(TempTargetInfo->PackageName)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
TempTargetInfo->CredTypes = (LPDWORD)( ((LPBYTE)(TempTargetInfo->CredTypes)) -
((LPBYTE)(TempTargetInfo)) +
((LPBYTE)UserTargetInfo) );
//
// Copy the target info to the user's address space
//
Status = LsaFunctions->CopyToClientBuffer(
NULL,
TempTargetInfoSize,
UserTargetInfo,
TempTargetInfo );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
//
// Copy the struct itself to the user's address space
//
TargetInfo.TargetInformation = UserTargetInfo;
Status = LsaFunctions->CopyToClientBuffer(
NULL,
sizeof(TargetInfo),
Buffer,
&TargetInfo );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
break;
default:
Status = STATUS_NOT_SUPPORTED;
break;
}
Cleanup:
if (Context != NULL) {
SspContextDereferenceContext( Context );
}
if ( TempTargetInfo != NULL ) {
CredFree( TempTargetInfo );
}
SspPrint((SSP_API, "Leaving SpQueryLsaModeContextAttributes for ctxt:0x%x, Attr:0x%x\n", ContextHandle, ContextAttribute));
return (SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
//+-------------------------------------------------------------------------
//
// Function: SpSetContextAttributes
//
// Synopsis: Set attributes of the specified context
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpSetContextAttributes(
IN LSA_SEC_HANDLE ContextHandle,
IN ULONG ContextAttribute,
IN PVOID Buffer,
IN ULONG BufferSize
)
{
NTSTATUS Status = STATUS_SUCCESS;
PSSP_CONTEXT Context = NULL;
PSecPkgContext_CredentialNameW CredentialNameInfo;
ULONG CredentialNameSize;
LPBYTE LocalBuffer = NULL;
SspPrint((SSP_API, "Entering SpSetContextAttributes for ctxt:0x%x, Attr:0x%x\n", ContextHandle, ContextAttribute));
//
// Find the currently existing context.
//
Status = SspContextReferenceContext( ContextHandle, FALSE, &Context );
if ( !NT_SUCCESS(Status) )
{
SspPrint(( SSP_CRITICAL,
"SpSetContextAttributes: invalid context handle.\n" ));
goto Cleanup;
}
//
// Grab a local copy of the data
//
// Sanity check this size before allocating
LocalBuffer = (LPBYTE) NtLmAllocatePrivateHeap( BufferSize );
if ( LocalBuffer == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = LsaFunctions->CopyFromClientBuffer(
NULL,
BufferSize,
LocalBuffer,
Buffer );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
//
// Return the appropriate information
//
switch(ContextAttribute) {
case SECPKG_ATTR_USE_VALIDATED:
if ( BufferSize != sizeof(SavedUseValidated) ) {
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
SavedUseValidated = *(LPDWORD)LocalBuffer;
break;
case SECPKG_ATTR_CREDENTIAL_NAME:
if ( BufferSize <= sizeof(SecPkgContext_CredentialNameW) ) {
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
// Sanity check the pointer and the contained string.
CredentialNameInfo = (PSecPkgContext_CredentialNameW) LocalBuffer;
SavedCredentialType = CredentialNameInfo->CredentialType;
// I'm guessing at the offset of the string.
wcscpy( SavedCredentialName, (LPWSTR)(CredentialNameInfo+1) );
break;
default:
Status = STATUS_NOT_SUPPORTED;
break;
}
Cleanup:
if (Context != NULL) {
SspContextDereferenceContext( Context );
}
if ( LocalBuffer != NULL ) {
NtLmFreePrivateHeap( LocalBuffer );
}
SspPrint((SSP_API, "Leaving SpSetContextAttributes for ctxt:0x%x, Attr:0x%x\n", ContextHandle, ContextAttribute));
return (SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
#endif