You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4477 lines
142 KiB
4477 lines
142 KiB
/*++
|
|
|
|
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 <kerberos.h>
|
|
|
|
#include "msp.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;
|
|
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 |
|
|
NTLMSSP_NEGOTIATE_VERSION;
|
|
|
|
|
|
|
|
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 ( (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) ||
|
|
(Context->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL)
|
|
)
|
|
{
|
|
//
|
|
// allows us to support gss-style seal/unseal for LDAP.
|
|
//
|
|
|
|
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM2;
|
|
}
|
|
|
|
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 ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
NegotiateMessage->Version = NTLMSSP_ENGINE_VERSION;
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: SppGenerateExplicitCredAudit
|
|
//
|
|
// Synopsis: Generates audit event when a logon is initiated by
|
|
// explicitly supplying credentials.
|
|
//
|
|
// Arguments: pContext -- pointer to SSP context
|
|
// pChallengeMessage -- pointer to challenge message. the target
|
|
// information is extracted from this.
|
|
// ChallengeMessageSize -- size of the above
|
|
// DoUnicode -- whether the target info is in
|
|
// unicode or ascii format.
|
|
//
|
|
// Returns: NTSTATUS code
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SppGenerateExplicitCredAudit(
|
|
IN PSSP_CONTEXT pContext,
|
|
IN PCHALLENGE_MESSAGE pChallengeMessage,
|
|
IN ULONG ChallengeMessageSize,
|
|
IN BOOLEAN DoUnicode
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
UNICODE_STRING TargetName = { 0 };
|
|
UNICODE_STRING TargetInfo = { 0 };
|
|
PUNICODE_STRING pTargetName = NULL;
|
|
PUNICODE_STRING pTargetInfo = NULL;
|
|
PWSTR pszTemp = NULL;
|
|
|
|
ASSERT( DoUnicode && L"SppGenerateExplicitCredAudit: DoUnicode is FALSE" );
|
|
|
|
//
|
|
// we do not handle non-unicode for now.
|
|
//
|
|
|
|
if ( !pContext || !pContext->Credential || !DoUnicode)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// extract target info from ChallengeMessage
|
|
//
|
|
|
|
if ((pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO) &&
|
|
(pChallengeMessage->TargetInfo.Length != 0) &&
|
|
(pChallengeMessage->TargetInfo.Buffer != NULL))
|
|
{
|
|
//
|
|
// target info is present. convert to absolute format now.
|
|
//
|
|
|
|
if (SspConvertRelativeToAbsolute (
|
|
pChallengeMessage,
|
|
ChallengeMessageSize,
|
|
&pChallengeMessage->TargetInfo,
|
|
(PSTRING)&TargetInfo,
|
|
DoUnicode,
|
|
TRUE // NULL target info OK
|
|
))
|
|
{
|
|
//
|
|
// get the DNS computer name
|
|
//
|
|
Status = MsvpAvlToString(
|
|
&TargetInfo,
|
|
MsvAvDnsComputerName,
|
|
&pszTemp
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) && pszTemp )
|
|
{
|
|
//
|
|
// initialize the target name/info to the same value
|
|
//
|
|
|
|
RtlInitUnicodeString( &TargetInfo, pszTemp );
|
|
RtlInitUnicodeString( &TargetName, pszTemp );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// DNS name not available, get the Netbios computer name
|
|
//
|
|
|
|
Status = MsvpAvlToString(
|
|
&TargetInfo,
|
|
MsvAvNbComputerName,
|
|
&pszTemp
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) && pszTemp )
|
|
{
|
|
//
|
|
// initialize the target name/info to the same value
|
|
//
|
|
|
|
RtlInitUnicodeString( &TargetInfo, pszTemp );
|
|
RtlInitUnicodeString( &TargetName, pszTemp );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SppGenerateExplicitCredAudit: "
|
|
"ChallengeMessage.TargetInfo size wrong %ld\n",
|
|
ChallengeMessageSize ));
|
|
}
|
|
}
|
|
|
|
if ( TargetInfo.Length && TargetName.Length )
|
|
{
|
|
pTargetInfo = &TargetInfo;
|
|
pTargetName = &TargetName;
|
|
}
|
|
//
|
|
// target info not available or could not be extracted,
|
|
// but we may have "domain name\0servername" (or "servername")
|
|
// in TargetName instead, use that if available.
|
|
//
|
|
else if ((pChallengeMessage->TargetName.Length != 0) &&
|
|
(pChallengeMessage->TargetName.Buffer != NULL))
|
|
{
|
|
if (SspConvertRelativeToAbsolute (
|
|
pChallengeMessage,
|
|
ChallengeMessageSize,
|
|
&pChallengeMessage->TargetName,
|
|
(PSTRING)&TargetName,
|
|
DoUnicode,
|
|
TRUE // NULL target info OK
|
|
))
|
|
{
|
|
//
|
|
// determine the TargetName format
|
|
// "domain name\0servername" or "servername"
|
|
//
|
|
|
|
PWSTR psz = TargetName.Buffer;
|
|
USHORT cb = 0;
|
|
|
|
while ((cb < TargetName.Length) && *psz)
|
|
{
|
|
psz++;
|
|
cb += sizeof(WCHAR);
|
|
}
|
|
|
|
if ( cb < TargetName.Length )
|
|
{
|
|
//
|
|
// we have "domain name\0servername", skip the
|
|
// domain name part.
|
|
//
|
|
|
|
psz++;
|
|
TargetName.Buffer = psz;
|
|
TargetName.Length = TargetName.Length - cb;
|
|
TargetName.MaximumLength = TargetName.Length;
|
|
}
|
|
|
|
pTargetName = pTargetInfo = &TargetName;
|
|
|
|
}
|
|
else
|
|
{
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SppGenerateExplicitCredAudit: "
|
|
"ChallengeMessage.TargetName size wrong %ld\n",
|
|
ChallengeMessageSize ));
|
|
}
|
|
}
|
|
|
|
{
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
LsaFunctions->GetCallInfo(&CallInfo);
|
|
|
|
|
|
Status = I_LsaIAuditLogonUsingExplicitCreds(
|
|
EVENTLOG_AUDIT_SUCCESS,
|
|
&pContext->Credential->LogonId, // user1 logon id
|
|
NULL, // user1 logon guid
|
|
(HANDLE) (ULONG_PTR) CallInfo.ProcessId,
|
|
&pContext->UserName,
|
|
&pContext->DomainName,
|
|
NULL, // user2 logon guid (NULL for ntlm)
|
|
pTargetName,
|
|
pTargetInfo
|
|
);
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( pszTemp )
|
|
{
|
|
NtLmFree( pszTemp );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
NTSTATUS AuditStatus = STATUS_SUCCESS;
|
|
|
|
MSV1_0_GETCHALLENRESP_REQUEST TempChallengeResponse;
|
|
PMSV1_0_GETCHALLENRESP_REQUEST GetChallengeResponse;
|
|
ULONG GetChallengeResponseSize;
|
|
|
|
UNICODE_STRING RevealedPassword = {0};
|
|
|
|
ULONG ChallengeResponseSize;
|
|
ULONG AuthenticateMessageSize;
|
|
PCHAR Where;
|
|
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 |
|
|
NTLMSSP_NEGOTIATE_VERSION;
|
|
|
|
|
|
*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;
|
|
}
|
|
|
|
|
|
//
|
|
// passing Accept handle to Init can cause 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)) {
|
|
|
|
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;
|
|
}
|
|
|
|
if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) {
|
|
if (InputTokenSize >= RTL_SIZEOF_THROUGH_FIELD(CHALLENGE_MESSAGE, Version)) {
|
|
|
|
C_ASSERT(sizeof(NTLM_VER_INFO) == sizeof(ULONG64));
|
|
|
|
RtlCopyMemory(&Context->ServerVersion, &ChallengeMessage->Version, sizeof(ChallengeMessage->Version));
|
|
|
|
SspPrint(( SSP_VERSION,
|
|
"SsprHandleChallengeMessage: ServerVersion %#I64x, Major %I64d, Minor %I64d, Build %I64d, Revision %I64d\n",
|
|
ChallengeMessage->Version,
|
|
Context->ServerVersion.Major,
|
|
Context->ServerVersion.Minor,
|
|
Context->ServerVersion.Build,
|
|
Context->ServerVersion.Revision ));
|
|
|
|
} else {
|
|
SecStatus = SEC_E_INVALID_TOKEN;
|
|
SspPrint(( SSP_CRITICAL,
|
|
"SsprHandleChallengeMessage: ChallengeMessage size too small with NTLMSSP_NEGOTIATE_VERSION\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 < RTL_SIZEOF_THROUGH_FIELD(CHALLENGE_MESSAGE, TargetInfo) ) {
|
|
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};
|
|
BOOLEAN bIsOwfPassword = FALSE;
|
|
SECPKG_CALL_INFO CallInfo = {0};
|
|
|
|
CredTargetInfo.NetbiosDomainName = szCredTargetDomain;
|
|
CredTargetInfo.NetbiosServerName = szCredTargetServer;
|
|
CredTargetInfo.DnsDomainName = szCredTargetDnsDomain;
|
|
CredTargetInfo.DnsServerName = szCredTargetDnsServer;
|
|
CredTargetInfo.DnsTreeName = szCredTargetDnsTree;
|
|
CredTargetInfo.PackageName = NTLMSP_NAME;
|
|
CredTargetInfo.CredTypeCount = COUNTOF(CredTypes);
|
|
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,
|
|
TRUE, // allow OWF passwords
|
|
&bIsOwfPassword
|
|
);
|
|
|
|
if ( NT_SUCCESS(SecStatus) )
|
|
{
|
|
fCredmanCredentials = TRUE;
|
|
if (bIsOwfPassword)
|
|
{
|
|
GetChallengeResponse->ParameterControl |= GCR_USE_OWF_PASSWORD;
|
|
}
|
|
}
|
|
|
|
if ( SecStatus == STATUS_NOT_FOUND )
|
|
{
|
|
SecStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(SecStatus) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// stow away a copy of the marshalled target info.
|
|
//
|
|
|
|
if (!LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
SecStatus = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( fCredmanCredentials || (CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE) )
|
|
{
|
|
CredMarshalTargetInfo(
|
|
&CredTargetInfo,
|
|
(PUSHORT*)&(Context->pbMarshalledTargetInfo),
|
|
&Context->cbMarshalledTargetInfo
|
|
);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// if an explicit cred was supplied, check if it was a marshalled
|
|
// cred, and if so, reject smartcard attempts.
|
|
//
|
|
|
|
if ( Context->UserName.Length != 0 &&
|
|
Context->DomainName.Length == 0
|
|
// Password may or may not contain a PIN for cert case.
|
|
)
|
|
{
|
|
UNICODE_STRING UnpickledUserName;
|
|
UNICODE_STRING UnpickledDomainName;
|
|
UNICODE_STRING UnpickledPassword;
|
|
|
|
Status = CredpProcessUserNameCredential(
|
|
&Context->UserName,
|
|
&UnpickledUserName,
|
|
&UnpickledDomainName,
|
|
&UnpickledPassword
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
NtLmFreePrivateHeap( UnpickledUserName.Buffer );
|
|
NtLmFreePrivateHeap( UnpickledDomainName.Buffer );
|
|
NtLmFreePrivateHeap( UnpickledPassword.Buffer );
|
|
|
|
}
|
|
else if ( Status == STATUS_NOT_SUPPORTED )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
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)))) {
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// DataGram can not negotiate if it is non NTLM2, allow to use LM key
|
|
//
|
|
|
|
if (Context->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM)
|
|
{
|
|
//
|
|
// handle smart servers that know to use NtUserSessionKey
|
|
//
|
|
|
|
if (Context->ServerVersion.Revision >= NTLMSSP_REVISION_W2K3 && NtLmGlobalLmProtocolSupported >= NoLm)
|
|
{
|
|
SspPrint((SSP_WARNING, "SsprHandleChallengeMessage turning off LM keys\n"));
|
|
|
|
GetChallengeResponse->ParameterControl &= ~RETURN_NON_NT_USER_SESSION_KEY;
|
|
Context->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY; // tell the server we turn off LM key
|
|
}
|
|
else // let it through, this might fail if NoLmHash is stored in SAM
|
|
{
|
|
GetChallengeResponse->ParameterControl |= GCR_ALLOW_LM;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!DoUnicode) {
|
|
GetChallengeResponse->ParameterControl |= GCR_USE_OEM_SET;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
AuthenticateMessage->Version = NTLMSSP_ENGINE_VERSION;
|
|
|
|
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;
|
|
|
|
ASSERT((AuthenticateMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) && L"NTLMSSP_NEGOTIATE_VERSION should be set");
|
|
|
|
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;
|
|
|
|
//
|
|
// Generate the explicit cred used audit for non null sessions
|
|
//
|
|
// LocalLoopBack uses implicit credentials only
|
|
//
|
|
|
|
if (NtChallengeResponse.Length) // both local loopback and null session have NtChallengeResponse zero length
|
|
{
|
|
AuditStatus = SppGenerateExplicitCredAudit(
|
|
Context,
|
|
ChallengeMessage,
|
|
InputTokenSize,
|
|
DoUnicode
|
|
);
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
IN CREDENTIAL_TARGET_INFORMATIONW* pTargetInfo,
|
|
IN OUT PSSP_CONTEXT Context,
|
|
IN BOOLEAN fShareLevel,
|
|
IN BOOLEAN bAllowOwfPassword,
|
|
OUT BOOLEAN* pbIsOwfPassword
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
fShareLevel - whether it is for sharelevel
|
|
|
|
bAllowOwfPassword - whether to look for OWF password, this prevents
|
|
username/domainname in Context from being trashed
|
|
|
|
pbIsOwfPassword - whether the cred found consists of OWFs
|
|
|
|
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;
|
|
PCREDENTIALW pPasswordForCertCred = NULL;
|
|
ULONG CredentialCount;
|
|
WCHAR* UserName = NULL;
|
|
WCHAR* DomainName = NULL;
|
|
UNICODE_STRING TempString = {0};
|
|
WCHAR szTempBuffer[UNLEN + UNLEN + 4]; // to hold domain\username or username@dnsdomainname
|
|
ENCRYPTED_CREDENTIALW* pCertCred = NULL;
|
|
KERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE* pQuerySuppCredResp = NULL;
|
|
|
|
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_WARNING, "CopyCredManCredentials: CrediReadDomainCredentials returned %x, LogonId %#x:%#x\n",
|
|
Status, LogonId->HighPart, LogonId->LowPart));
|
|
|
|
ASSERT(
|
|
(Status == STATUS_NO_SUCH_LOGON_SESSION)||
|
|
(Status == STATUS_INVALID_PARAMETER)||
|
|
(Status == STATUS_NOT_FOUND)
|
|
);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// NULL terminate scatch buffer
|
|
//
|
|
|
|
szTempBuffer[COUNTOF(szTempBuffer) - 1] = L'\0';
|
|
|
|
//
|
|
// Loop through the list of credentials
|
|
//
|
|
|
|
for ( ULONG CredIndex = 0; CredIndex < CredentialCount; CredIndex++ )
|
|
{
|
|
//
|
|
// NTLM only supports password credentials
|
|
// skip OWF passwords if not allowed
|
|
//
|
|
|
|
if ( (Credentials[CredIndex]->Type != CRED_TYPE_DOMAIN_PASSWORD)
|
|
|| (!bAllowOwfPassword && (Credentials[CredIndex]->Flags & CRED_FLAGS_OWF_CRED_BLOB)) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// For Share level connects, don't allow matching against * creds.
|
|
//
|
|
// CRED_FLAGS_PASSWORD_FOR_CERT cred is *Session cred
|
|
//
|
|
|
|
if ( fShareLevel )
|
|
{
|
|
if ( (Credentials[CredIndex]->TargetName) &&
|
|
(wcschr( Credentials[CredIndex]->TargetName, L'*' ) != NULL) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((Credentials[CredIndex]->Flags & CRED_FLAGS_PASSWORD_FOR_CERT))
|
|
{
|
|
pPasswordForCertCred = Credentials[CredIndex];
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Convert the UserName to domain name and user name
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
szTempBuffer,
|
|
Credentials[CredIndex]->UserName,
|
|
sizeof(WCHAR) * min(wcslen(Credentials[CredIndex]->UserName) + 1, COUNTOF(szTempBuffer) - 1)
|
|
);
|
|
Status = CredpParseUserName( szTempBuffer, &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;
|
|
}
|
|
|
|
SspPrint((SSP_CRED, "CopyCredManCredentials copying credmain passwords\n"));
|
|
|
|
TempString.Buffer = (PWSTR) Credentials[CredIndex]->CredentialBlob;
|
|
TempString.MaximumLength = (USHORT) Credentials[CredIndex]->CredentialBlobSize;
|
|
TempString.Length = (USHORT) EncryptedCredentials[CredIndex]->ClearCredentialBlobSize;
|
|
|
|
|
|
//
|
|
// 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"";
|
|
}
|
|
|
|
//
|
|
// Free the existing password and add the new one
|
|
//
|
|
|
|
if (Context->Password.Buffer)
|
|
{
|
|
NtLmFreePrivateHeap (Context->Password.Buffer);
|
|
Context->Password.Buffer = NULL;
|
|
}
|
|
|
|
Status = NtLmDuplicatePassword(&Context->Password, &TempString);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pbIsOwfPassword = (BOOLEAN) (Credentials[CredIndex]->Flags & CRED_FLAGS_OWF_CRED_BLOB);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// now call kerberos to get password for cert cred
|
|
//
|
|
|
|
if (pPasswordForCertCred)
|
|
{
|
|
KERB_QUERY_SUPPLEMENTAL_CREDS_REQUEST QuerySuppCred;
|
|
UNICODE_STRING KerberosPackageName = CONSTANT_UNICODE_STRING((MICROSOFT_KERBEROS_NAME_W));
|
|
UNICODE_STRING MsvPackageName = CONSTANT_UNICODE_STRING((NTLMSP_NAME));
|
|
ULONG cbResponse = 0;
|
|
NTSTATUS SubStatus = STATUS_UNSUCCESSFUL;
|
|
ENCRYPTED_CREDENTIALW* pPasswordCred = NULL;
|
|
|
|
SspPrint((SSP_CRED, "CopyCredManCredentials querying passwords for cert %ws\n", pPasswordForCertCred->TargetName));
|
|
|
|
Status = LsaFunctions->CrediRead(
|
|
LogonId,
|
|
CREDP_FLAGS_IN_PROCESS, // CredFlags
|
|
pPasswordForCertCred->TargetName,
|
|
CRED_TYPE_DOMAIN_CERTIFICATE, // CredType
|
|
0, // no Flags
|
|
&pCertCred
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory(&QuerySuppCred, sizeof(QuerySuppCred));
|
|
|
|
QuerySuppCred.MessageType = KerbQuerySupplementalCredentialsMessage;
|
|
QuerySuppCred.PackageName = MsvPackageName;
|
|
QuerySuppCred.MarshalledCreds = (CREDENTIALW *) pCertCred;
|
|
QuerySuppCred.LogonId = *LogonId;
|
|
|
|
Status = LsaFunctions->CallPackage(
|
|
&KerberosPackageName,
|
|
&QuerySuppCred,
|
|
sizeof(QuerySuppCred),
|
|
reinterpret_cast<VOID**>(&pQuerySuppCredResp),
|
|
&cbResponse,
|
|
&SubStatus
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SspPrint((SSP_WARNING, "CopyCredManCredentials CallPackage failed with Status %#x\n", Status));
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!NT_SUCCESS(SubStatus))
|
|
{
|
|
SspPrint((SSP_WARNING, "CopyCredManCredentials CallPackage failed with SubStatus %#x\n", SubStatus));
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT( pQuerySuppCredResp );
|
|
|
|
pPasswordCred = &pQuerySuppCredResp->ReturnedCreds;
|
|
|
|
//
|
|
// NTLM only supports password credentials and Sanity check the
|
|
// credential
|
|
//
|
|
|
|
if ( (pPasswordCred->Cred.Type != CRED_TYPE_DOMAIN_PASSWORD) ||
|
|
(pPasswordCred->Cred.Flags & CRED_FLAGS_PASSWORD_FOR_CERT) ||
|
|
(!bAllowOwfPassword && (pPasswordCred->Cred.Flags & CRED_FLAGS_OWF_CRED_BLOB)) )
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pPasswordCred->Cred.Flags & CRED_FLAGS_PROMPT_NOW)
|
|
{
|
|
Status = SEC_E_LOGON_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pPasswordCred->Cred.UserName == NULL)
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SspPrint((SSP_CRED, "CopyCredManCredentials using domain\\user %ws\n", pPasswordCred->Cred.UserName));
|
|
|
|
//
|
|
// Convert the UserName to domain name and user name
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
szTempBuffer,
|
|
pPasswordCred->Cred.UserName,
|
|
sizeof(WCHAR) * min(wcslen(pPasswordCred->Cred.UserName) + 1, COUNTOF(szTempBuffer) - 1)
|
|
);
|
|
Status = CredpParseUserName( szTempBuffer, &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;
|
|
}
|
|
|
|
TempString.Buffer = (PWSTR) pPasswordCred->Cred.CredentialBlob;
|
|
TempString.MaximumLength = (USHORT) pPasswordCred->Cred.CredentialBlobSize;
|
|
TempString.Length = (USHORT) pPasswordCred->ClearCredentialBlobSize;
|
|
|
|
//
|
|
// Free the existing password and add the new one
|
|
//
|
|
|
|
if (Context->Password.Buffer)
|
|
{
|
|
NtLmFreePrivateHeap(Context->Password.Buffer);
|
|
Context->Password.Buffer = NULL;
|
|
}
|
|
|
|
Status = NtLmDuplicatePassword(&Context->Password, &TempString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pbIsOwfPassword = (BOOLEAN) (pPasswordCred->Cred.Flags & CRED_FLAGS_OWF_CRED_BLOB);
|
|
|
|
//
|
|
// now this cred is good to go, write it into credman before we return
|
|
//
|
|
|
|
//
|
|
// we consider failures of CrediWrite non-fatal, ignore the return code
|
|
//
|
|
|
|
(VOID) LsaFunctions->CrediWrite(
|
|
LogonId,
|
|
CREDP_FLAGS_IN_PROCESS, // CredFlags
|
|
pPasswordCred,
|
|
0 // no Flags
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
SspPrint((SSP_CRED, "CopyCredManCredentials no credman credential found\n"));
|
|
|
|
Status = STATUS_NOT_FOUND;
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Free the returned credentials
|
|
//
|
|
|
|
if (pCertCred)
|
|
{
|
|
LsaFunctions->FreeLsaHeap(pCertCred);
|
|
}
|
|
|
|
if (EncryptedCredentials)
|
|
{
|
|
LsaFunctions->CrediFreeCredentials(
|
|
CredentialCount,
|
|
EncryptedCredentials
|
|
);
|
|
}
|
|
|
|
if (pQuerySuppCredResp)
|
|
{
|
|
LsaFunctions->FreeLsaHeap(pQuerySuppCredResp);
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SspPrint((SSP_WARNING, "CopyCredManCredentials failed to get credman password Status %#x\n", Status));
|
|
}
|
|
#endif // DBG
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CredpExtractMarshalledTargetInfo(
|
|
IN PUNICODE_STRING TargetServerName,
|
|
OUT CREDENTIAL_TARGET_INFORMATIONW **pTargetInfo
|
|
)
|
|
{
|
|
CREDENTIAL_TARGET_INFORMATIONW *NewTargetInfo;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// 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) < CRED_MARSHALED_TI_SIZE_SIZE )
|
|
)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Unmarshal the target info
|
|
//
|
|
|
|
Status = CredUnmarshalTargetInfo (
|
|
TargetServerName->Buffer,
|
|
TargetServerName->MaximumLength,
|
|
&NewTargetInfo,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
if ( Status == STATUS_INVALID_PARAMETER )
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( *pTargetInfo != NULL )
|
|
{
|
|
LocalFree( *pTargetInfo );
|
|
}
|
|
*pTargetInfo = NewTargetInfo;
|
|
}
|
|
|
|
|
|
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;
|
|
BOOLEAN bIsOwfPassword = FALSE;
|
|
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) )
|
|
{
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
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,
|
|
FALSE, // do not allow OWF passwords
|
|
&bIsOwfPassword
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// do not support OWF password via UserNameCredentials
|
|
//
|
|
|
|
ASSERT(!bIsOwfPassword);
|
|
|
|
*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
|