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.
2480 lines
71 KiB
2480 lines
71 KiB
//+--------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
|
//
|
|
// File: ntlm.cxx
|
|
//
|
|
// Contents: main entrypoints for the ntlm security package
|
|
// SpLsaModeInitialize
|
|
// SpInitialize
|
|
// SpShutdown
|
|
// SpGetInfo
|
|
//
|
|
// Helper functions:
|
|
// NtLmSetPolicyInfo
|
|
// NtLmPolicyChangeCallback
|
|
// NtLmRegisterForPolicyChange
|
|
// NtLmUnregisterForPolicyChange
|
|
//
|
|
// History: ChandanS 26-Jul-1996 Stolen from kerberos\client2\kerberos.cxx
|
|
// ChandanS 16-Apr-1998 No reboot on domain name change
|
|
// JClark 28-Jun-2000 Added WMI Trace Logging Support
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
// Variables with the EXTERN storage class are declared here
|
|
#define NTLM_GLOBAL
|
|
#define DEBUG_ALLOCATE
|
|
|
|
#include <global.h>
|
|
#include <wow64t.h>
|
|
#include "trace.h"
|
|
|
|
extern "C"
|
|
{
|
|
|
|
#include "nlp.h"
|
|
|
|
NTSTATUS
|
|
LsaApInitializePackage(
|
|
IN ULONG AuthenticationPackageId,
|
|
IN PLSA_DISPATCH_TABLE LsaDispatchTable,
|
|
IN PSTRING Database OPTIONAL,
|
|
IN PSTRING Confidentiality OPTIONAL,
|
|
OUT PSTRING *AuthenticationPackageName
|
|
);
|
|
}
|
|
|
|
BOOLEAN NtLmCredentialInitialized;
|
|
BOOLEAN NtLmContextInitialized;
|
|
BOOLEAN NtLmRNGInitialized;
|
|
|
|
LIST_ENTRY NtLmProcessOptionsList;
|
|
RTL_RESOURCE NtLmProcessOptionsLock;
|
|
|
|
|
|
|
|
//+--------------------------------------------------------------------
|
|
//
|
|
// Function: SpLsaModeInitialize
|
|
//
|
|
// Synopsis: This function is called by the LSA when this DLL is loaded.
|
|
// It returns security package function tables for all
|
|
// security packages in the DLL.
|
|
//
|
|
// Arguments: LsaVersion - Version number of the LSA
|
|
// PackageVersion - Returns version number of the package
|
|
// Tables - Returns array of function tables for the package
|
|
// TableCount - Returns number of entries in array of
|
|
// function tables.
|
|
//
|
|
// Returns: PackageVersion (as above)
|
|
// Tables (as above)
|
|
// TableCount (as above)
|
|
//
|
|
// Notes:
|
|
//
|
|
//---------------------------------------------------------------------
|
|
NTSTATUS NTAPI
|
|
SpLsaModeInitialize(
|
|
IN ULONG LsaVersion,
|
|
OUT PULONG PackageVersion,
|
|
OUT PSECPKG_FUNCTION_TABLE * Tables,
|
|
OUT PULONG TableCount
|
|
)
|
|
{
|
|
#if DBG
|
|
// SspGlobalDbflag = SSP_CRITICAL| SSP_API| SSP_API_MORE |SSP_INIT| SSP_MISC | SSP_NO_LOCAL;
|
|
SspGlobalDbflag = SSP_CRITICAL;
|
|
InitializeCriticalSection(&SspGlobalLogFileCritSect);
|
|
#endif
|
|
SspPrint((SSP_API, "Entering SpLsaModeInitialize\n"));
|
|
|
|
SECURITY_STATUS Status = SEC_E_OK;
|
|
|
|
if (LsaVersion != SECPKG_INTERFACE_VERSION)
|
|
{
|
|
SspPrint((SSP_CRITICAL, "Invalid LSA version: %d\n", LsaVersion));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
|
|
NtLmFunctionTable.InitializePackage = NULL;
|
|
NtLmFunctionTable.LogonUser = NULL;
|
|
NtLmFunctionTable.CallPackage = LsaApCallPackage;
|
|
NtLmFunctionTable.LogonTerminated = LsaApLogonTerminated;
|
|
NtLmFunctionTable.CallPackageUntrusted = LsaApCallPackageUntrusted;
|
|
NtLmFunctionTable.LogonUserEx = NULL;
|
|
NtLmFunctionTable.LogonUserEx2 = LsaApLogonUserEx2;
|
|
NtLmFunctionTable.Initialize = SpInitialize;
|
|
NtLmFunctionTable.Shutdown = SpShutdown;
|
|
NtLmFunctionTable.GetInfo = SpGetInfo;
|
|
NtLmFunctionTable.AcceptCredentials = SpAcceptCredentials;
|
|
NtLmFunctionTable.AcquireCredentialsHandle = SpAcquireCredentialsHandle;
|
|
NtLmFunctionTable.FreeCredentialsHandle = SpFreeCredentialsHandle;
|
|
NtLmFunctionTable.SaveCredentials = SpSaveCredentials;
|
|
NtLmFunctionTable.GetCredentials = SpGetCredentials;
|
|
NtLmFunctionTable.DeleteCredentials = SpDeleteCredentials;
|
|
NtLmFunctionTable.InitLsaModeContext = SpInitLsaModeContext;
|
|
NtLmFunctionTable.AcceptLsaModeContext = SpAcceptLsaModeContext;
|
|
NtLmFunctionTable.DeleteContext = SpDeleteContext;
|
|
NtLmFunctionTable.ApplyControlToken = SpApplyControlToken;
|
|
NtLmFunctionTable.GetUserInfo = SpGetUserInfo;
|
|
NtLmFunctionTable.QueryCredentialsAttributes = SpQueryCredentialsAttributes ;
|
|
NtLmFunctionTable.GetExtendedInformation = SpGetExtendedInformation ;
|
|
NtLmFunctionTable.SetExtendedInformation = SpSetExtendedInformation ;
|
|
NtLmFunctionTable.CallPackagePassthrough = LsaApCallPackagePassthrough;
|
|
#if 0
|
|
NtLmFunctionTable.QueryContextAttributes = SpQueryLsaModeContextAttributes;
|
|
NtLmFunctionTable.SetContextAttributes = SpSetContextAttributes;
|
|
|
|
*PackageVersion = SECPKG_INTERFACE_VERSION_2;
|
|
#else
|
|
*PackageVersion = SECPKG_INTERFACE_VERSION;
|
|
#endif
|
|
|
|
*Tables = &NtLmFunctionTable;
|
|
*TableCount = 1;
|
|
|
|
//
|
|
// Get the Event Trace logging on board
|
|
//
|
|
NtlmInitializeTrace();
|
|
|
|
SafeAllocaInitialize(SAFEALLOCA_USE_DEFAULT,
|
|
SAFEALLOCA_USE_DEFAULT,
|
|
NtLmAllocate,
|
|
NtLmFree);
|
|
|
|
CleanUp:
|
|
|
|
SspPrint((SSP_API, "Leaving SpLsaModeInitialize\n"));
|
|
|
|
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: NtlmGetRandomOemName
|
|
//
|
|
// Synopsis: Fix up to get good looking yet pseudo random OEM names
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//+-------------------------------------------------------------------------
|
|
VOID
|
|
NtlmGetRandomOemName(
|
|
IN ULONG OemNameBufferSize,
|
|
IN OUT CHAR* OemNameBuffer
|
|
)
|
|
{
|
|
CHAR Scratch[13] = {0}; // 4 * 3 bytes + an NULL
|
|
CHAR EncodedScratch[((RTL_NUMBER_OF(Scratch) - 1) / 3) * 4 + 1] = {0}; // 4 * 4 bytes + an NULL
|
|
|
|
const UCHAR abEncode[] =
|
|
/* 0 thru 25: */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
/* 26 thru 51: */ "!($%)'*+,-./:;#=&?[]_^{}|~"
|
|
/* 52 thru 61: */ "0123456789"
|
|
/* 62 and 63: */ "<>";
|
|
|
|
C_ASSERT((RTL_NUMBER_OF(abEncode) >= 64));
|
|
C_ASSERT((RTL_NUMBER_OF(Scratch) - 1) % 3 == 0);
|
|
C_ASSERT(RTL_NUMBER_OF(EncodedScratch) > CNLEN);
|
|
|
|
SspGenerateRandomBits(Scratch, RTL_NUMBER_OF(Scratch) - 1);
|
|
|
|
ASSERT(OemNameBufferSize >= CNLEN);
|
|
|
|
if (OemNameBufferSize < CNLEN)
|
|
{
|
|
OemNameBuffer[OemNameBufferSize - 1] = '\0';
|
|
return;
|
|
}
|
|
|
|
for (ULONG j = 0; j < (RTL_NUMBER_OF(Scratch) - 1) / 3; j++)
|
|
{
|
|
EncodedScratch[j * 4 + 0] = RtlUpperChar(abEncode[(Scratch[j * 3 + 0] >> 2) & 0x3f]); // first 6 bits
|
|
EncodedScratch[j * 4 + 1] = RtlUpperChar(abEncode[((Scratch[j * 3 + 0] << 4) | (Scratch[j * 3 + 1] >> 4)) & 0x3f]); // the second 6 bits
|
|
EncodedScratch[j * 4 + 2] = RtlUpperChar(abEncode[((Scratch[j * 3 + 1] << 2) | (Scratch[j * 3 + 2] >> 6)) & 0x3f]); // the third 6 bits
|
|
EncodedScratch[j * 4 + 3] = RtlUpperChar(abEncode[Scratch[j * 3 + 2] & 0x3f]); // the last 6 bits
|
|
}
|
|
|
|
RtlCopyMemory(OemNameBuffer, EncodedScratch, CNLEN - 1);
|
|
OemNameBuffer[CNLEN - 1] = '\0';
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: NtLmSetPolicyInfo
|
|
//
|
|
// Synopsis: Function to be called when policy changes
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
// if fInit is TRUE, this is called by the init routine in ntlm
|
|
//
|
|
//
|
|
//+-------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NtLmSetPolicyInfo(
|
|
IN PUNICODE_STRING DnsComputerName,
|
|
IN PUNICODE_STRING ComputerName,
|
|
IN PUNICODE_STRING DnsDomainName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PSID DomainSid,
|
|
IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass,
|
|
IN BOOLEAN fInit
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
// Buffers to delete on cleanup
|
|
|
|
STRING ComputerNameAnsiString;
|
|
STRING DomainNameAnsiString;
|
|
|
|
UNICODE_STRING DnsTreeName = {0};
|
|
|
|
ComputerNameAnsiString.Buffer = NULL;
|
|
DomainNameAnsiString.Buffer = NULL;
|
|
|
|
static TimeStamp NtlmSetPolicyInfoTime = {0};
|
|
static ULONG NumOfUpdatedLuids = 0;
|
|
|
|
NtQuerySystemTime(&NtlmSetPolicyInfoTime);
|
|
|
|
//
|
|
// grab the treename. don't do this during Init, because the SAM
|
|
// isn't initialized yet.
|
|
//
|
|
|
|
if (!fInit)
|
|
{
|
|
Status = SsprQueryTreeName( &DnsTreeName );
|
|
}
|
|
|
|
RtlAcquireResourceExclusive(&NtLmGlobalCritSect, TRUE);
|
|
|
|
if (!fInit && NT_SUCCESS( Status ))
|
|
{
|
|
if ( NtLmGlobalUnicodeDnsTreeName.Buffer != NULL )
|
|
{
|
|
NtLmFree( NtLmGlobalUnicodeDnsTreeName.Buffer );
|
|
}
|
|
|
|
RtlCopyMemory(&NtLmGlobalUnicodeDnsTreeName, &DnsTreeName, sizeof(DnsTreeName));
|
|
}
|
|
|
|
//
|
|
// Do this only if this is package init
|
|
//
|
|
|
|
if (fInit)
|
|
{
|
|
if (ComputerName && ComputerName->Buffer != NULL)
|
|
{
|
|
ULONG cLength = ComputerName->Length / sizeof(WCHAR);
|
|
|
|
if ((ComputerName->Length + sizeof(WCHAR)) > sizeof(NtLmGlobalUnicodeComputerName))
|
|
{
|
|
// Bad ComputerName
|
|
Status = STATUS_INVALID_COMPUTER_NAME;
|
|
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Bad computer name length is %d\n", cLength));
|
|
goto CleanUp;
|
|
}
|
|
|
|
wcsncpy(NtLmGlobalUnicodeComputerName,
|
|
ComputerName->Buffer,
|
|
cLength);
|
|
|
|
NtLmGlobalUnicodeComputerName[cLength] = UNICODE_NULL;
|
|
|
|
// make NtlmGlobalUnicodeComputerNameString a string form
|
|
|
|
RtlInitUnicodeString( &NtLmGlobalUnicodeComputerNameString,
|
|
NtLmGlobalUnicodeComputerName );
|
|
|
|
// Save old buffers for deleting
|
|
ComputerNameAnsiString = NtLmGlobalOemComputerNameString;
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString(
|
|
&NtLmGlobalOemComputerNameString,
|
|
&NtLmGlobalUnicodeComputerNameString,
|
|
TRUE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
ComputerNameAnsiString.Buffer = NULL;
|
|
|
|
NtlmGetRandomOemName(RTL_NUMBER_OF(NtLmGlobalOemComputerName), NtLmGlobalOemComputerName);
|
|
|
|
RtlInitString(&NtLmGlobalOemComputerNameString, NtLmGlobalOemComputerName);
|
|
|
|
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo error from RtlUpcaseUnicodeStringToOemString is %#x, random computer name is %s\n",
|
|
Status, NtLmGlobalOemComputerName));
|
|
|
|
ASSERT(!L"Non OEM compatible computer name encountered");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize various forms of the primary domain name of the local system
|
|
// Do this only if this is package init or it's DnsDomain info
|
|
//
|
|
|
|
if (fInit || (ChangedInfoClass == PolicyNotifyDnsDomainInformation))
|
|
{
|
|
if (DnsComputerName && DnsComputerName->Buffer != NULL ) {
|
|
ULONG cLength = DnsComputerName->Length / sizeof(WCHAR);
|
|
|
|
if((DnsComputerName->Length + sizeof(WCHAR)) > sizeof(NtLmGlobalUnicodeDnsComputerName))
|
|
{
|
|
// Bad ComputerName
|
|
Status = STATUS_INVALID_COMPUTER_NAME;
|
|
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Bad computer name length is %d\n", cLength));
|
|
goto CleanUp;
|
|
}
|
|
|
|
wcsncpy(NtLmGlobalUnicodeDnsComputerName,
|
|
DnsComputerName->Buffer,
|
|
cLength);
|
|
|
|
NtLmGlobalUnicodeDnsComputerName[cLength] = UNICODE_NULL;
|
|
|
|
// make NtlmGlobalUnicodeDnsComputerNameString a string form
|
|
|
|
RtlInitUnicodeString( &NtLmGlobalUnicodeDnsComputerNameString,
|
|
NtLmGlobalUnicodeDnsComputerName );
|
|
}
|
|
|
|
if (DnsDomainName && DnsDomainName->Buffer != NULL ) {
|
|
ULONG cLength = DnsDomainName->Length / sizeof(WCHAR);
|
|
|
|
if((DnsDomainName->Length + sizeof(WCHAR)) > sizeof(NtLmGlobalUnicodeDnsDomainName))
|
|
{
|
|
// Bad ComputerName
|
|
Status = STATUS_INVALID_COMPUTER_NAME;
|
|
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Bad domain name length is %d\n", cLength));
|
|
goto CleanUp;
|
|
}
|
|
|
|
wcsncpy(NtLmGlobalUnicodeDnsDomainName,
|
|
DnsDomainName->Buffer,
|
|
cLength);
|
|
|
|
NtLmGlobalUnicodeDnsDomainName[cLength] = UNICODE_NULL;
|
|
|
|
// make NtlmGlobalUnicodeDnsDomainNameString a string form
|
|
|
|
RtlInitUnicodeString( &NtLmGlobalUnicodeDnsDomainNameString,
|
|
NtLmGlobalUnicodeDnsDomainName );
|
|
}
|
|
|
|
if (DomainName && DomainName->Buffer != NULL)
|
|
{
|
|
ULONG cLength = DomainName->Length / sizeof(WCHAR);
|
|
|
|
if ((DomainName->Length + sizeof(WCHAR)) > sizeof(NtLmGlobalUnicodePrimaryDomainName))
|
|
{
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Bad domain name length is %d\n", cLength));
|
|
goto CleanUp;
|
|
}
|
|
wcsncpy(NtLmGlobalUnicodePrimaryDomainName,
|
|
DomainName->Buffer,
|
|
cLength);
|
|
NtLmGlobalUnicodePrimaryDomainName[cLength] = UNICODE_NULL;
|
|
|
|
// make NtlmGlobalUnicodePrimaryDomainNameString a string form
|
|
|
|
RtlInitUnicodeString( &NtLmGlobalUnicodePrimaryDomainNameString,
|
|
NtLmGlobalUnicodePrimaryDomainName );
|
|
|
|
// Save old buffers for deleting
|
|
DomainNameAnsiString = NtLmGlobalOemPrimaryDomainNameString;
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString(
|
|
&NtLmGlobalOemPrimaryDomainNameString,
|
|
&NtLmGlobalUnicodePrimaryDomainNameString,
|
|
TRUE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
DomainNameAnsiString.Buffer = NULL;
|
|
|
|
NtlmGetRandomOemName(RTL_NUMBER_OF(NtLmGlobalOemPrimaryDomainName), NtLmGlobalOemPrimaryDomainName);
|
|
|
|
RtlInitString(&NtLmGlobalOemPrimaryDomainNameString, NtLmGlobalOemPrimaryDomainName);
|
|
|
|
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo error from RtlUpcaseUnicodeStringToOemString is %#x, random domain name is %s\n",
|
|
Status, NtLmGlobalOemPrimaryDomainName));
|
|
|
|
ASSERT(!L"Non OEM compatible domain name encountered");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a standalone windows NT workstation,
|
|
// use the computer name as the Target name.
|
|
//
|
|
|
|
if ( DomainSid != NULL)
|
|
{
|
|
NtLmGlobalUnicodeTargetName = NtLmGlobalUnicodePrimaryDomainNameString;
|
|
NtLmGlobalOemTargetName = NtLmGlobalOemPrimaryDomainNameString;
|
|
NtLmGlobalTargetFlags = NTLMSSP_TARGET_TYPE_DOMAIN;
|
|
NtLmGlobalDomainJoined = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NtLmGlobalUnicodeTargetName = NtLmGlobalUnicodeComputerNameString;
|
|
NtLmGlobalOemTargetName = NtLmGlobalOemComputerNameString;
|
|
NtLmGlobalTargetFlags = NTLMSSP_TARGET_TYPE_SERVER;
|
|
NtLmGlobalDomainJoined = FALSE;
|
|
}
|
|
|
|
//
|
|
// update the GlobalNtlm3 targetinfo.
|
|
//
|
|
|
|
Status = SsprUpdateTargetInfo();
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// update the domain names and credentials if this is the really policy callback
|
|
//
|
|
|
|
if (!fInit)
|
|
{
|
|
PACTIVE_LOGON ActiveLogon = NULL;
|
|
LUID MachineSecretLuids[] = {NETWORKSERVICE_LUID, NtLmGlobalLuidMachineLogon};
|
|
|
|
PMSV1_0_PRIMARY_CREDENTIAL PrimaryCredential = NULL;
|
|
ULONG PrimaryCredentialSize = 0;
|
|
PMSV1_0_PRIMARY_CREDENTIAL NewPrimaryCredential = NULL;
|
|
ULONG NewPrimaryCredentialSize = 0;
|
|
PACTIVE_LOGON NewActiveLogon = NULL;
|
|
ULONG NewActiveLogonSize = 0;
|
|
UCHAR* Where = NULL;
|
|
|
|
NlpLockActiveLogonsWrite();
|
|
|
|
for (ULONG i = 0; i < RTL_NUMBER_OF(MachineSecretLuids); i++)
|
|
{
|
|
if (PrimaryCredential)
|
|
{
|
|
RtlZeroMemory(PrimaryCredential, PrimaryCredentialSize);
|
|
LsaFunctions->FreeLsaHeap( PrimaryCredential );
|
|
PrimaryCredential = NULL;
|
|
PrimaryCredentialSize = 0;
|
|
}
|
|
|
|
if (NewPrimaryCredential)
|
|
{
|
|
RtlZeroMemory(NewPrimaryCredential, NewPrimaryCredentialSize);
|
|
LsaFunctions->FreeLsaHeap( NewPrimaryCredential );
|
|
NewPrimaryCredential = NULL;
|
|
NewPrimaryCredentialSize = 0;
|
|
}
|
|
|
|
if (NewActiveLogon)
|
|
{
|
|
I_NtLmFree(NewActiveLogon);
|
|
NewActiveLogon = NULL;
|
|
}
|
|
|
|
ActiveLogon = NlpFindActiveLogon(MachineSecretLuids + i);
|
|
|
|
if (ActiveLogon)
|
|
{
|
|
//
|
|
// check for fake update
|
|
//
|
|
|
|
if (RtlEqualDomainName(
|
|
&ActiveLogon->LogonDomainName,
|
|
&NtLmGlobalUnicodePrimaryDomainNameString
|
|
))
|
|
{
|
|
SspPrint((SSP_UPDATES, "NtLmSetPolicyInfo domain name %wZ not really changed\n", &ActiveLogon->LogonDomainName));
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
SspPrint((SSP_UPDATES,
|
|
"NtLmSetPolicyInfo domain name of %#x:%#x is changed from %wZ to %wZ\n",
|
|
ActiveLogon->LogonId.HighPart, ActiveLogon->LogonId.LowPart,
|
|
&ActiveLogon->LogonDomainName,
|
|
&NtLmGlobalUnicodePrimaryDomainNameString));
|
|
}
|
|
|
|
//
|
|
// Allocate an entry for the active logon table.
|
|
//
|
|
|
|
NewActiveLogonSize = ROUND_UP_COUNT(sizeof(ACTIVE_LOGON), ALIGN_QUAD)
|
|
+ ROUND_UP_COUNT(RtlLengthSid(ActiveLogon->UserSid), ALIGN_QUAD)
|
|
+ ActiveLogon->UserName.Length + sizeof(WCHAR)
|
|
+ NtLmGlobalUnicodePrimaryDomainNameString.Length + sizeof(WCHAR)
|
|
+ ActiveLogon->LogonServer.Length + sizeof(WCHAR);
|
|
|
|
NewActiveLogon = (ACTIVE_LOGON*) I_NtLmAllocate( NewActiveLogonSize );
|
|
|
|
if ( NewActiveLogon == NULL )
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
SspPrint((SSP_CRITICAL, "NtlmSetPolicyInfo %#x No memory %#x\n", i, NewActiveLogonSize));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Fill in the logon table entry
|
|
//
|
|
|
|
Where = (PUCHAR) (NewActiveLogon + 1);
|
|
NewActiveLogon->Signature = NTLM_ACTIVE_LOGON_MAGIC_SIGNATURE;
|
|
|
|
OLD_TO_NEW_LARGE_INTEGER(
|
|
ActiveLogon->LogonId,
|
|
NewActiveLogon->LogonId );
|
|
|
|
NewActiveLogon->Flags = ActiveLogon->Flags;
|
|
NewActiveLogon->LogonType = ActiveLogon->LogonType;
|
|
|
|
//
|
|
// copy DWORD aligned fields first
|
|
//
|
|
|
|
Where = (UCHAR*) ROUND_UP_POINTER( Where, ALIGN_DWORD );
|
|
Status = RtlCopySid(RtlLengthSid(ActiveLogon->UserSid), (PSID)Where, ActiveLogon->UserSid);
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtlmSetPolicyInfo: RtlLengthSid for %#x:%#x failed with %#x, NewActiveLogon %p, ActiveLogon %p, Where %p\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart, Status, NewActiveLogon, ActiveLogon, Where));
|
|
|
|
ASSERT(!L"RtlCopySid should not fail");
|
|
break;
|
|
}
|
|
|
|
NewActiveLogon->UserSid = (PSID) Where;
|
|
Where += RtlLengthSid(ActiveLogon->UserSid);
|
|
|
|
//
|
|
// Copy WCHAR aligned fields
|
|
//
|
|
|
|
Where = (UCHAR*) ROUND_UP_POINTER( Where, ALIGN_WCHAR );
|
|
NlpPutString( &NewActiveLogon->UserName,
|
|
&ActiveLogon->UserName,
|
|
&Where );
|
|
|
|
NlpPutString( &NewActiveLogon->LogonDomainName,
|
|
&NtLmGlobalUnicodePrimaryDomainNameString, // new domain name
|
|
&Where );
|
|
|
|
NlpPutString( &NewActiveLogon->LogonServer,
|
|
&ActiveLogon->LogonServer,
|
|
&Where );
|
|
|
|
//
|
|
// Get the next enumeration handle for this session
|
|
//
|
|
|
|
NewActiveLogon->EnumHandle = ActiveLogon->EnumHandle;
|
|
|
|
//
|
|
// remove the old entry
|
|
//
|
|
|
|
RemoveEntryList(&ActiveLogon->ListEntry);
|
|
|
|
//
|
|
// Insert new entry into the active logon table
|
|
//
|
|
|
|
InsertTailList(&NlpActiveLogonListAnchor, &NewActiveLogon->ListEntry);
|
|
|
|
//
|
|
// release ownership
|
|
//
|
|
|
|
NewActiveLogon = NULL;
|
|
|
|
I_NtLmFree(ActiveLogon);
|
|
|
|
ActiveLogon = NULL;
|
|
|
|
//
|
|
// Update credentials as well
|
|
//
|
|
|
|
Status = NlpGetPrimaryCredential( MachineSecretLuids + i,
|
|
&PrimaryCredential,
|
|
&PrimaryCredentialSize );
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
SspPrint((SSP_WARNING, "NtlmSetPolicyInfo: NlpDeletePrimaryCredential for %#x:%#x failed with %#x\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart, Status));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
ASSERT(PrimaryCredential && L"Should have PrimaryCredential at this point");
|
|
|
|
Status = NlpMakePrimaryCredential(
|
|
&NtLmGlobalUnicodePrimaryDomainNameString,
|
|
&NtLmGlobalUnicodeComputerNameString,
|
|
&NtLmGlobalUnicodeComputerNameString, // ok use machine name as password
|
|
&NewPrimaryCredential,
|
|
&NewPrimaryCredentialSize
|
|
);
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtlmSetPolicyInfo: NlpMakePrimaryCredential for %#x:%#x failed with %#x\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart, Status));
|
|
ASSERT(!L"NlpMakePrimaryCredential should not fail");
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// set the passwords in it
|
|
//
|
|
|
|
NewPrimaryCredential->LmPasswordPresent = PrimaryCredential->LmPasswordPresent;
|
|
NewPrimaryCredential->LmOwfPassword = PrimaryCredential->LmOwfPassword;
|
|
|
|
NewPrimaryCredential->NtPasswordPresent = PrimaryCredential->NtPasswordPresent;
|
|
NewPrimaryCredential->NtOwfPassword = PrimaryCredential->NtOwfPassword;
|
|
|
|
NewPrimaryCredential->ShaPasswordPresent = PrimaryCredential->ShaPasswordPresent;
|
|
NewPrimaryCredential->ShaOwfPassword = PrimaryCredential->ShaOwfPassword;
|
|
|
|
ASSERT(NewPrimaryCredential && L"Should have NewPrimaryCredential at this point");
|
|
|
|
//
|
|
// Delete it from the LSA
|
|
//
|
|
|
|
Status = NlpDeletePrimaryCredential( MachineSecretLuids + i );
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtlmSetPolicyInfo: NlpDeletePrimaryCredential for %#x:%#x failed with %#x\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart, Status));
|
|
ASSERT(!L"NlpDeletePrimaryCredential should not fail");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add it back to the LSA.
|
|
//
|
|
|
|
Status = NlpAddPrimaryCredential(
|
|
MachineSecretLuids + i,
|
|
NewPrimaryCredential,
|
|
NewPrimaryCredentialSize
|
|
);
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtlmSetPolicyInfo: NlpAddPrimaryCredential for %#x:%#x failed with %#x\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart, Status));
|
|
ASSERT(!L"NlpAddPrimaryCredential should not fail");
|
|
break;
|
|
}
|
|
|
|
SspPrint((SSP_UPDATES,
|
|
"NtlmSetPolicyInfo successfully updated %#x:%#x\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart));
|
|
|
|
NumOfUpdatedLuids++;
|
|
}
|
|
else
|
|
{
|
|
SspPrint((SSP_UPDATES,
|
|
"NtlmSetPolicyInfo no updates: %#x:%#x does not exist\n",
|
|
MachineSecretLuids[i].HighPart, MachineSecretLuids[i].LowPart));
|
|
}
|
|
}
|
|
|
|
NlpUnlockActiveLogons();
|
|
|
|
if (PrimaryCredential)
|
|
{
|
|
RtlZeroMemory(PrimaryCredential, PrimaryCredentialSize);
|
|
LsaFunctions->FreeLsaHeap( PrimaryCredential );
|
|
}
|
|
|
|
if (NewPrimaryCredential)
|
|
{
|
|
RtlZeroMemory(NewPrimaryCredential, NewPrimaryCredentialSize);
|
|
LsaFunctions->FreeLsaHeap( NewPrimaryCredential );
|
|
}
|
|
|
|
if (NewActiveLogon)
|
|
{
|
|
I_NtLmFree(NewActiveLogon);
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
RtlReleaseResource(&NtLmGlobalCritSect);
|
|
|
|
if (ComputerNameAnsiString.Buffer)
|
|
{
|
|
RtlFreeOemString(&ComputerNameAnsiString);
|
|
}
|
|
|
|
if (DomainNameAnsiString.Buffer)
|
|
{
|
|
RtlFreeOemString(&DomainNameAnsiString);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NtLmFlushLogonCache (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function flushes the logon cache. This is done on unjoin.
|
|
|
|
If the cache were not flushed, a user could logon to cached credentials after the unjoin.
|
|
That is especially bad since Winlogon now tries a cached logon to improve boot times.
|
|
|
|
Return Value:
|
|
|
|
NERR_Success -- Success
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
HKEY hKey = NULL;
|
|
|
|
#define NETSETUPP_LOGON_CACHE_PATH L"SECURITY\\Cache"
|
|
#define NETSETUPP_LOGON_CACHE_VALUE L"NL$Control"
|
|
|
|
|
|
//
|
|
// Open the key containing the cache
|
|
//
|
|
|
|
NetStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
NETSETUPP_LOGON_CACHE_PATH,
|
|
0,
|
|
KEY_SET_VALUE,
|
|
&hKey );
|
|
|
|
if ( NetStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Delete the value describing the size of the cache
|
|
// This ensures the values cannot be used
|
|
//
|
|
|
|
RegDeleteValue( hKey, NETSETUPP_LOGON_CACHE_VALUE );
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: NtLmPolicyChangeCallback
|
|
//
|
|
// Synopsis: Function to be called when domain policy changes
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID NTAPI
|
|
NtLmPolicyChangeCallback(
|
|
IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLSAPR_POLICY_INFORMATION Policy = NULL;
|
|
GUID GuidNull = {0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
switch (ChangedInfoClass)
|
|
{
|
|
case PolicyNotifyDnsDomainInformation:
|
|
{
|
|
|
|
WCHAR UnicodeDnsComputerName[DNS_MAX_NAME_LENGTH + 1];
|
|
UNICODE_STRING UnicodeDnsComputerNameString;
|
|
ULONG DnsComputerNameLength = sizeof(UnicodeDnsComputerName) / sizeof(WCHAR);
|
|
|
|
//
|
|
// Get the new domain information
|
|
//
|
|
|
|
|
|
Status = I_LsaIQueryInformationPolicyTrusted(
|
|
PolicyDnsDomainInformation,
|
|
&Policy
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtLmPolicyChangeCallback, Error from I_LsaIQueryInformationPolicyTrusted is %d\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( Policy->PolicyDnsDomainInfo.DomainGuid == GuidNull )
|
|
{
|
|
NtLmGlobalDownlevelDomain = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NtLmGlobalDownlevelDomain = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// get the new DNS computer name
|
|
//
|
|
|
|
if ( !GetComputerNameExW( ComputerNameDnsFullyQualified,
|
|
UnicodeDnsComputerName,
|
|
&DnsComputerNameLength ) )
|
|
{
|
|
UnicodeDnsComputerName[ 0 ] = L'\0';
|
|
}
|
|
|
|
RtlInitUnicodeString( &UnicodeDnsComputerNameString,
|
|
UnicodeDnsComputerName);
|
|
|
|
|
|
Status = NtLmSetPolicyInfo(
|
|
&UnicodeDnsComputerNameString,
|
|
NULL,
|
|
(PUNICODE_STRING) &Policy->PolicyDnsDomainInfo.DnsDomainName,
|
|
(PUNICODE_STRING) &Policy->PolicyDnsDomainInfo.Name,
|
|
(PSID) Policy->PolicyDnsDomainInfo.Sid,
|
|
ChangedInfoClass,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtLmPolicyChangeCallback, Error from NtLmSetDomainName is %d\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
{
|
|
BOOLEAN FlushLogonCache = FALSE;
|
|
|
|
if( NtLmSecPkg.DomainSid == NULL &&
|
|
Policy->PolicyDnsDomainInfo.Sid != NULL
|
|
)
|
|
{
|
|
FlushLogonCache = TRUE;
|
|
}
|
|
|
|
if( NtLmSecPkg.DomainSid != NULL &&
|
|
Policy->PolicyDnsDomainInfo.Sid == NULL
|
|
)
|
|
{
|
|
FlushLogonCache = TRUE;
|
|
}
|
|
|
|
if( NtLmSecPkg.DomainSid != NULL &&
|
|
Policy->PolicyDnsDomainInfo.Sid != NULL
|
|
)
|
|
{
|
|
if(!RtlEqualSid( NtLmSecPkg.DomainSid, Policy->PolicyDnsDomainInfo.Sid ))
|
|
{
|
|
FlushLogonCache = TRUE;
|
|
}
|
|
}
|
|
|
|
if( FlushLogonCache )
|
|
{
|
|
//
|
|
// flush the logon cache...
|
|
//
|
|
|
|
NtLmFlushLogonCache();
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
|
|
if (Policy != NULL)
|
|
{
|
|
switch (ChangedInfoClass)
|
|
{
|
|
case PolicyNotifyDnsDomainInformation:
|
|
{
|
|
I_LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyDnsDomainInformation,
|
|
Policy
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: NtLmRegisterForPolicyChange
|
|
//
|
|
// Synopsis: Register with the LSA to be notified of policy changes
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
NtLmRegisterForPolicyChange(
|
|
IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
Status = I_LsaIRegisterPolicyChangeNotificationCallback(
|
|
NtLmPolicyChangeCallback,
|
|
ChangedInfoClass
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "NtLmRegisterForPolicyChange, Error from I_LsaIRegisterPolicyChangeNotificationCallback is %d\n", Status));
|
|
}
|
|
SspPrint((SSP_MISC, "I_LsaIRegisterPolicyChangeNotificationCallback called with %d\n", ChangedInfoClass));
|
|
return(Status);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: NtLmUnregisterForPolicyChange
|
|
//
|
|
// Synopsis: Unregister for policy change notification
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
NtLmUnregisterForPolicyChange(
|
|
IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass
|
|
)
|
|
{
|
|
(VOID) I_LsaIUnregisterPolicyChangeNotificationCallback(
|
|
NtLmPolicyChangeCallback,
|
|
ChangedInfoClass
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------
|
|
//
|
|
// Function: SpInitialize
|
|
//
|
|
// Synopsis: Initializes the Security package
|
|
//
|
|
// Arguments: PackageId - Contains ID for this package assigned by LSA
|
|
// Parameters - Contains machine-specific information
|
|
// FunctionTable - Contains table of LSA helper routines
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Notes: Everything that was done in LsaApInitializePackage
|
|
// should be done here. Lsa assures us that only
|
|
// one thread is executing this at a time. Don't
|
|
// have to worry about concurrency problems.
|
|
// Most of the stuff was taken from SspCommonInitialize()
|
|
// from svcdlls\ntlmssp\common\initcomn.c
|
|
//
|
|
//---------------------------------------------------------------------
|
|
NTSTATUS NTAPI
|
|
SpInitialize(
|
|
IN ULONG_PTR PackageId,
|
|
IN PSECPKG_PARAMETERS Parameters,
|
|
IN PLSA_SECPKG_FUNCTION_TABLE FunctionTable
|
|
)
|
|
{
|
|
SspPrint((SSP_API, "Entering SpInitialize\n"));
|
|
|
|
SECURITY_STATUS Status = SEC_E_OK;
|
|
WCHAR UnicodeComputerName[CNLEN + 1];
|
|
UNICODE_STRING UnicodeComputerNameString;
|
|
ULONG ComputerNameLength =
|
|
(sizeof(UnicodeComputerName)/sizeof(WCHAR));
|
|
|
|
WCHAR UnicodeDnsComputerName[DNS_MAX_NAME_LENGTH + 1];
|
|
UNICODE_STRING UnicodeDnsComputerNameString;
|
|
ULONG DnsComputerNameLength = sizeof(UnicodeDnsComputerName) / sizeof(WCHAR);
|
|
GUID GuidNull = {0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
//
|
|
// Init the global crit section
|
|
//
|
|
|
|
__try
|
|
{
|
|
RtlInitializeResource(&NtLmGlobalCritSect);
|
|
|
|
RtlInitializeResource(&NtLmProcessOptionsLock);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto CleanUp;
|
|
}
|
|
|
|
InitializeListHead( &NtLmProcessOptionsList );
|
|
|
|
//
|
|
// All the following are global
|
|
//
|
|
|
|
NtLmState = NtLmLsaMode;
|
|
NtLmPackageId = PackageId;
|
|
|
|
// We really need this to be a day less than maxtime so when callers
|
|
// of sspi convert to utc, they won't get time in the past.
|
|
|
|
NtLmGlobalForever.HighPart = 0x7FFFFF36;
|
|
NtLmGlobalForever.LowPart = 0xD5969FFF;
|
|
|
|
//
|
|
// Following are local
|
|
//
|
|
|
|
NtLmCredentialInitialized = FALSE;
|
|
NtLmContextInitialized = FALSE;
|
|
NtLmRNGInitialized = FALSE;
|
|
|
|
//
|
|
// Save away the Lsa functions
|
|
//
|
|
|
|
LsaFunctions = FunctionTable;
|
|
|
|
//
|
|
// Save the Parameters info
|
|
//
|
|
|
|
NtLmSecPkg.MachineState = Parameters->MachineState;
|
|
NtLmSecPkg.SetupMode = Parameters->SetupMode;
|
|
|
|
//
|
|
// allocate a locally unique ID rereferencing the machine logon.
|
|
//
|
|
|
|
Status = NtAllocateLocallyUniqueId( &NtLmGlobalLuidMachineLogon );
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtAllocateLocallyUniqueId is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// create a logon session for the machine logon.
|
|
//
|
|
Status = LsaFunctions->CreateLogonSession( &NtLmGlobalLuidMachineLogon );
|
|
if( !NT_SUCCESS(Status) ) {
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from CreateLogonSession is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
Status = NtLmDuplicateUnicodeString(
|
|
&NtLmSecPkg.DomainName,
|
|
&Parameters->DomainName);
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmDuplicateUnicodeString is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
Status = NtLmDuplicateUnicodeString(
|
|
&NtLmSecPkg.DnsDomainName,
|
|
&Parameters->DnsDomainName);
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmDuplicateUnicodeString is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (Parameters->DomainSid != NULL) {
|
|
Status = NtLmDuplicateSid( &NtLmSecPkg.DomainSid,
|
|
Parameters->DomainSid );
|
|
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmDuplicateSid is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine if this machine is running NT Workstation or NT Server
|
|
//
|
|
|
|
if (!RtlGetNtProductType (&NtLmGlobalNtProductType))
|
|
{
|
|
SspPrint((SSP_API_MORE, "RtlGetNtProductType defaults to NtProductWinNt\n"));
|
|
}
|
|
|
|
//
|
|
// Determine if we are running Personal SKU
|
|
//
|
|
|
|
{
|
|
OSVERSIONINFOEXW osvi;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
|
|
if(GetVersionExW((OSVERSIONINFOW*)&osvi))
|
|
{
|
|
NtLmGlobalPersonalSKU = ( osvi.wProductType == VER_NT_WORKSTATION && (osvi.wSuiteMask & VER_SUITE_PERSONAL));
|
|
} else {
|
|
SspPrint((SSP_API_MORE, "GetVersionEx defaults to non-personal\n"));
|
|
}
|
|
}
|
|
|
|
|
|
Status = I_LsaIOpenPolicyTrusted(&NtLmGlobalPolicyHandle);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from I_LsaIOpenPolicyTrusted is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if ( !GetComputerNameW( UnicodeComputerName,
|
|
&ComputerNameLength ) ) {
|
|
Status = STATUS_INVALID_COMPUTER_NAME;
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from GetComputerNameW is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if ( !GetComputerNameExW( ComputerNameDnsFullyQualified,
|
|
UnicodeDnsComputerName,
|
|
&DnsComputerNameLength ) )
|
|
{
|
|
|
|
//
|
|
// per CliffV, failure is legal.
|
|
//
|
|
|
|
UnicodeDnsComputerName[ 0 ] = L'\0';
|
|
}
|
|
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&siaNtAuthority,
|
|
1,
|
|
SECURITY_ANONYMOUS_LOGON_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&NtLmGlobalAnonymousSid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// pickup a copy of the Local System access token.
|
|
//
|
|
|
|
{
|
|
HANDLE hProcessToken;
|
|
NTSTATUS StatusToken;
|
|
|
|
NtLmGlobalAccessTokenSystem = NULL;
|
|
|
|
StatusToken = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_QUERY | TOKEN_DUPLICATE,
|
|
&hProcessToken
|
|
);
|
|
|
|
if ( NT_SUCCESS( StatusToken ) )
|
|
{
|
|
TOKEN_STATISTICS LocalTokenStatistics;
|
|
DWORD TokenStatisticsSize = sizeof(LocalTokenStatistics);
|
|
LUID LogonIdSystem = SYSTEM_LUID;
|
|
|
|
Status = NtQueryInformationToken(
|
|
hProcessToken,
|
|
TokenStatistics,
|
|
&LocalTokenStatistics,
|
|
TokenStatisticsSize,
|
|
&TokenStatisticsSize
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
//
|
|
// see if it's SYSTEM.
|
|
//
|
|
|
|
if (RtlEqualLuid(
|
|
&LogonIdSystem,
|
|
&(LocalTokenStatistics.AuthenticationId)
|
|
))
|
|
{
|
|
Status = SspDuplicateToken(
|
|
hProcessToken,
|
|
SecurityImpersonation,
|
|
&NtLmGlobalAccessTokenSystem
|
|
);
|
|
}
|
|
}
|
|
|
|
NtClose( hProcessToken );
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, could not acquire SYSTEM token %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Init the Credential stuff
|
|
//
|
|
|
|
Status = SspCredentialInitialize();
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from SspCredentialInitializeis %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
NtLmCredentialInitialized = TRUE;
|
|
|
|
//
|
|
// Init the Context stuff
|
|
//
|
|
Status = SspContextInitialize();
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from SspContextInitializeis %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
NtLmContextInitialized = TRUE;
|
|
|
|
//
|
|
// Get the locale and check if it is FRANCE, which doesn't allow
|
|
// encryption
|
|
//
|
|
|
|
NtLmGlobalEncryptionEnabled = IsEncryptionPermitted();
|
|
|
|
//
|
|
// Init the random number generator stuff
|
|
//
|
|
|
|
if( !NtLmInitializeRNG() ) {
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmInitializeRNG\n"));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto CleanUp;
|
|
}
|
|
NtLmRNGInitialized = TRUE;
|
|
|
|
NtLmCheckLmCompatibility();
|
|
|
|
if( NtLmSecPkg.DomainSid != NULL )
|
|
{
|
|
NtLmGlobalDomainJoined = TRUE;
|
|
}
|
|
|
|
if ( NtLmSecPkg.DomainGuid == GuidNull )
|
|
{
|
|
NtLmGlobalDownlevelDomain = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NtLmGlobalDownlevelDomain = FALSE;
|
|
}
|
|
|
|
NtLmQueryMappedDomains();
|
|
|
|
//
|
|
// Set all the globals relating to computer name, domain name, sid etc.
|
|
// This routine is also used by the callback for notifications from the lsa
|
|
//
|
|
|
|
RtlInitUnicodeString( &UnicodeComputerNameString,
|
|
UnicodeComputerName);
|
|
|
|
RtlInitUnicodeString( &UnicodeDnsComputerNameString,
|
|
UnicodeDnsComputerName);
|
|
|
|
Status = NtLmSetPolicyInfo( &UnicodeDnsComputerNameString,
|
|
&UnicodeComputerNameString,
|
|
&NtLmSecPkg.DnsDomainName,
|
|
&NtLmSecPkg.DomainName,
|
|
NtLmSecPkg.DomainSid,
|
|
PolicyNotifyAuditEventsInformation, // Ignored
|
|
TRUE ); // yes, package init
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmSetDomainInfo %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
// Do the Init stuff for the MSV authentication package
|
|
// Passing FunctionTable as a (PLSA_DISPATCH_TABLE).
|
|
// Well, the first 11 entries are the same. Cheating a
|
|
// bit.
|
|
|
|
Status = LsaApInitializePackage(
|
|
(ULONG) PackageId,
|
|
(PLSA_DISPATCH_TABLE)FunctionTable,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from LsaApInitializePackage is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
Status = NtLmRegisterForPolicyChange(PolicyNotifyDnsDomainInformation);
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmRegisterForPolicyChange is %d\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
Status = SspGenerateRandomBits(NtlmGlobalMagicNumber, MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SpInitialize, error in generating global magic number\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
SpShutdown();
|
|
}
|
|
|
|
SspPrint((SSP_API, "Leaving SpInitialize\n"));
|
|
|
|
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
|
|
}
|
|
|
|
//+--------------------------------------------------------------------
|
|
//
|
|
// Function: SpShutdown
|
|
//
|
|
// Synopsis: Exported function to shutdown the Security package.
|
|
//
|
|
// Effects: Forces the freeing of all credentials, contexts
|
|
// and frees all global data
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: SEC_E_OK in all cases
|
|
// Most of the stuff was taken from SspCommonShutdown()
|
|
// from svcdlls\ntlmssp\common\initcomn.c
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------
|
|
NTSTATUS NTAPI
|
|
SpShutdown(
|
|
VOID
|
|
)
|
|
{
|
|
SspPrint((SSP_API, "Entering SpShutdown\n"));
|
|
|
|
//
|
|
// comment out LSA mode cleanup code, per NTBUG 400026,
|
|
// which can result in access violations during shutdown when
|
|
// calls into package are still occuring during shutdown.
|
|
//
|
|
|
|
#if 0
|
|
|
|
if (NtLmContextInitialized)
|
|
{
|
|
SspContextTerminate();
|
|
NtLmContextInitialized = FALSE;
|
|
}
|
|
|
|
if (NtLmCredentialInitialized)
|
|
{
|
|
SspCredentialTerminate();
|
|
NtLmCredentialInitialized = FALSE;
|
|
}
|
|
|
|
if (NtLmGlobalOemComputerNameString.Buffer != NULL)
|
|
{
|
|
RtlFreeOemString(&NtLmGlobalOemComputerNameString);
|
|
NtLmGlobalOemComputerNameString.Buffer = NULL;
|
|
}
|
|
|
|
if (NtLmGlobalOemPrimaryDomainNameString.Buffer != NULL)
|
|
{
|
|
RtlFreeOemString(&NtLmGlobalOemPrimaryDomainNameString);
|
|
NtLmGlobalOemPrimaryDomainNameString.Buffer = NULL;
|
|
}
|
|
|
|
if (NtLmGlobalNtLm3TargetInfo.Buffer != NULL)
|
|
{
|
|
NtLmFree (NtLmGlobalNtLm3TargetInfo.Buffer);
|
|
NtLmGlobalNtLm3TargetInfo.Buffer = NULL;
|
|
}
|
|
|
|
if ( NtLmSecPkg.DomainName.Buffer != NULL )
|
|
{
|
|
NtLmFree (NtLmSecPkg.DomainName.Buffer);
|
|
}
|
|
|
|
if ( NtLmSecPkg.DnsDomainName.Buffer != NULL )
|
|
{
|
|
NtLmFree (NtLmSecPkg.DnsDomainName.Buffer);
|
|
}
|
|
|
|
if ( NtLmSecPkg.DomainSid != NULL )
|
|
{
|
|
NtLmFree (NtLmSecPkg.DomainSid);
|
|
}
|
|
|
|
if (NtLmGlobalLocalSystemSid != NULL)
|
|
{
|
|
FreeSid( NtLmGlobalLocalSystemSid);
|
|
NtLmGlobalLocalSystemSid = NULL;
|
|
}
|
|
|
|
if (NtLmGlobalAliasAdminsSid != NULL)
|
|
{
|
|
FreeSid( NtLmGlobalAliasAdminsSid);
|
|
NtLmGlobalAliasAdminsSid = NULL;
|
|
}
|
|
|
|
if (NtLmGlobalProcessUserSid != NULL)
|
|
{
|
|
NtLmFree( NtLmGlobalProcessUserSid );
|
|
NtLmGlobalProcessUserSid = NULL;
|
|
}
|
|
|
|
if( NtLmGlobalAnonymousSid )
|
|
{
|
|
FreeSid( NtLmGlobalAnonymousSid );
|
|
NtLmGlobalAnonymousSid = NULL;
|
|
}
|
|
|
|
|
|
if (NtLmRNGInitialized)
|
|
{
|
|
NtLmCleanupRNG();
|
|
NtLmRNGInitialized = FALSE;
|
|
}
|
|
|
|
NtLmFreeMappedDomains();
|
|
|
|
NtLmUnregisterForPolicyChange(PolicyNotifyDnsDomainInformation);
|
|
|
|
if (NtLmGlobalAccessTokenSystem != NULL) {
|
|
NtClose( NtLmGlobalAccessTokenSystem );
|
|
NtLmGlobalAccessTokenSystem = NULL;
|
|
}
|
|
|
|
RtlDeleteResource(&NtLmGlobalCritSect);
|
|
|
|
if (NtLmGlobalPolicyHandle != NULL) {
|
|
(VOID) I_LsarClose( &NtLmGlobalPolicyHandle );
|
|
}
|
|
|
|
SspPrint((SSP_API, "Leaving SpShutdown\n"));
|
|
#if DBG
|
|
DeleteCriticalSection(&SspGlobalLogFileCritSect);
|
|
#endif
|
|
|
|
#endif // NTBUG 400026
|
|
|
|
return(SEC_E_OK);
|
|
}
|
|
|
|
//+--------------------------------------------------------------------
|
|
//
|
|
// Function: SpGetInfo
|
|
//
|
|
// Synopsis: Returns information about the package
|
|
//
|
|
// Effects: returns pointers to global data
|
|
//
|
|
// Arguments: PackageInfo - Receives security package information
|
|
//
|
|
// Returns: SEC_E_OK in all cases
|
|
//
|
|
// Notes: Pointers to constants ok. Lsa will copy the data
|
|
// before sending it to someone else
|
|
//
|
|
//---------------------------------------------------------------------
|
|
NTSTATUS NTAPI
|
|
SpGetInfo(
|
|
OUT PSecPkgInfo PackageInfo
|
|
)
|
|
{
|
|
SspPrint((SSP_API, "Entering SpGetInfo\n"));
|
|
|
|
PackageInfo->fCapabilities = NTLMSP_CAPS;
|
|
PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
|
|
PackageInfo->wRPCID = RPC_C_AUTHN_WINNT;
|
|
PackageInfo->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
|
|
PackageInfo->Name = NTLMSP_NAME;
|
|
PackageInfo->Comment = NTLMSP_COMMENT;
|
|
|
|
SspPrint((SSP_API, "Leaving SpGetInfo\n"));
|
|
|
|
return(SEC_E_OK);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SpGetExtendedInformation(
|
|
IN SECPKG_EXTENDED_INFORMATION_CLASS Class,
|
|
OUT PSECPKG_EXTENDED_INFORMATION * ppInformation
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSECPKG_EXTENDED_INFORMATION Information = NULL;
|
|
ULONG Size ;
|
|
|
|
switch ( Class )
|
|
{
|
|
case SecpkgContextThunks:
|
|
|
|
Information = (PSECPKG_EXTENDED_INFORMATION)
|
|
NtLmAllocate(sizeof(SECPKG_EXTENDED_INFORMATION) + sizeof(DWORD));
|
|
if (Information == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
Information->Class = SecpkgContextThunks;
|
|
Information->Info.ContextThunks.InfoLevelCount = 1;
|
|
Information->Info.ContextThunks.Levels[0] = SECPKG_ATTR_CREDENTIAL_NAME;
|
|
*ppInformation = Information;
|
|
Information = NULL;
|
|
break;
|
|
|
|
case SecpkgWowClientDll:
|
|
|
|
//
|
|
// This indicates that we're smart enough to handle wow client processes
|
|
//
|
|
|
|
Information = (PSECPKG_EXTENDED_INFORMATION)
|
|
NtLmAllocate( sizeof( SECPKG_EXTENDED_INFORMATION ) +
|
|
(MAX_PATH * sizeof(WCHAR) ) );
|
|
|
|
if ( Information == NULL )
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES ;
|
|
goto Cleanup ;
|
|
}
|
|
|
|
Information->Class = SecpkgWowClientDll ;
|
|
Information->Info.WowClientDll.WowClientDllPath.Buffer = (PWSTR) (Information + 1);
|
|
Size = ExpandEnvironmentStrings(
|
|
L"%SystemRoot%\\" WOW64_SYSTEM_DIRECTORY_U L"\\msv1_0.DLL",
|
|
Information->Info.WowClientDll.WowClientDllPath.Buffer,
|
|
MAX_PATH );
|
|
Information->Info.WowClientDll.WowClientDllPath.Length = (USHORT) (Size * sizeof(WCHAR));
|
|
Information->Info.WowClientDll.WowClientDllPath.MaximumLength = (USHORT) ((Size + 1) * sizeof(WCHAR) );
|
|
*ppInformation = Information ;
|
|
Information = NULL ;
|
|
|
|
break;
|
|
|
|
default:
|
|
Status = SEC_E_UNSUPPORTED_FUNCTION ;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( Information != NULL )
|
|
{
|
|
NtLmFree( Information );
|
|
}
|
|
|
|
return Status ;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SpSetExtendedInformation(
|
|
IN SECPKG_EXTENDED_INFORMATION_CLASS Class,
|
|
IN PSECPKG_EXTENDED_INFORMATION Info
|
|
)
|
|
{
|
|
NTSTATUS Status ;
|
|
|
|
|
|
switch ( Class )
|
|
{
|
|
case SecpkgMutualAuthLevel:
|
|
NtLmGlobalMutualAuthLevel = Info->Info.MutualAuthLevel.MutualAuthLevel ;
|
|
Status = SEC_E_OK ;
|
|
break;
|
|
|
|
default:
|
|
Status = SEC_E_UNSUPPORTED_FUNCTION ;
|
|
break;
|
|
}
|
|
|
|
return Status ;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtLmCheckLmCompatibility(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if we should support the LM challenge
|
|
response protocol by looking in the registry under
|
|
system\currentcontrolset\Control\Lsa\LmCompatibilityLevel. The level
|
|
indicates whether to send the LM reponse by default and whether to
|
|
ever send the LM response
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus;
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
|
|
//
|
|
// initialize defaults
|
|
// Assume that LM is supported.
|
|
//
|
|
|
|
NtLmGlobalLmProtocolSupported = 0;
|
|
NtLmGlobalRequireNtlm2 = FALSE;
|
|
NtLmGlobalDatagramUse128BitEncryption = FALSE;
|
|
NtLmGlobalDatagramUse56BitEncryption = FALSE;
|
|
NtLmGlobalLsaKey = NULL;
|
|
NtLmGlobalLsaMsv1_0Key = NULL;
|
|
|
|
//
|
|
// Open the Lsa key in the registry
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&KeyName,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa"
|
|
);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
NtStatus = NtOpenKey(
|
|
&KeyHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// save away registry key so we can use it for notification events.
|
|
//
|
|
|
|
NtLmGlobalLsaKey = (HKEY)KeyHandle;
|
|
|
|
// now open the MSV1_0 subkey...
|
|
|
|
RtlInitUnicodeString(
|
|
&KeyName,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa\\Msv1_0"
|
|
);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
NtStatus = NtOpenKey(
|
|
&KeyHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// save away registry key so we can use it for notification events.
|
|
//
|
|
|
|
NtLmGlobalLsaMsv1_0Key = (HKEY)KeyHandle;
|
|
}
|
|
|
|
ULONG
|
|
NtLmValidMinimumSecurityFlagsMask(
|
|
IN ULONG MinimumSecurity
|
|
)
|
|
/*++
|
|
|
|
This routine takes a NtLmMinimumClientSec or NtLmMinimumServerSec registry
|
|
value and masks off the bits that are not relevant for enforcing the
|
|
supported options.
|
|
|
|
--*/
|
|
{
|
|
|
|
return (MinimumSecurity & (
|
|
NTLMSSP_NEGOTIATE_UNICODE |
|
|
NTLMSSP_NEGOTIATE_SIGN |
|
|
NTLMSSP_NEGOTIATE_SEAL |
|
|
NTLMSSP_NEGOTIATE_NTLM2 |
|
|
NTLMSSP_NEGOTIATE_128 |
|
|
NTLMSSP_NEGOTIATE_KEY_EXCH |
|
|
NTLMSSP_NEGOTIATE_56
|
|
));
|
|
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
NtLmQueryDynamicGlobals(
|
|
PVOID pvContext,
|
|
BOOLEAN TimedOut // whether this callback occurred because of a timer expiration
|
|
)
|
|
{
|
|
SspPrint((SSP_API, "Entering NtLmQueryDynamicGlobals\n"));
|
|
|
|
HKEY KeyHandle; // open registry key to Lsa\MSV1_0
|
|
LONG RegStatus;
|
|
|
|
DWORD RegValueType;
|
|
DWORD RegValue;
|
|
DWORD RegValueSize;
|
|
BOOLEAN PriorForceGuest;
|
|
|
|
KeyHandle = NtLmGlobalLsaKey;
|
|
|
|
if ( KeyHandle != NULL )
|
|
{
|
|
//
|
|
// lm compatibility level.
|
|
//
|
|
|
|
RegValueSize = sizeof( RegValue );
|
|
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"LmCompatibilityLevel",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR)&RegValue,
|
|
&RegValueSize
|
|
);
|
|
|
|
|
|
if ( RegStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ((RegValueSize >= sizeof(ULONG)) &&
|
|
(RegValueType == REG_DWORD)) {
|
|
|
|
NtLmGlobalLmProtocolSupported = (ULONG)RegValue;
|
|
}
|
|
} else if ( RegStatus == ERROR_FILE_NOT_FOUND ) {
|
|
|
|
//
|
|
// value was deleted - resort to default.
|
|
//
|
|
|
|
NtLmGlobalLmProtocolSupported = 0;
|
|
}
|
|
|
|
//
|
|
// handle ForceGuest
|
|
//
|
|
|
|
PriorForceGuest = NtLmGlobalForceGuest;
|
|
|
|
if ( NtLmGlobalNtProductType != NtProductLanManNt )
|
|
{
|
|
RegValueSize = sizeof( RegValue );
|
|
|
|
if ( NtLmGlobalPersonalSKU )
|
|
{
|
|
//
|
|
// personal product always has ForceGuest turned on.
|
|
//
|
|
|
|
RegValueSize = sizeof(ULONG);
|
|
RegValueType = REG_DWORD;
|
|
RegValue = 1;
|
|
RegStatus = ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
if ( NtLmGlobalDomainJoined )
|
|
{
|
|
//
|
|
// joined product always has ForceGuest turned off.
|
|
//
|
|
|
|
RegValueSize = sizeof(ULONG);
|
|
RegValueType = REG_DWORD;
|
|
RegValue = 0;
|
|
RegStatus = ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"ForceGuest",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR)&RegValue,
|
|
&RegValueSize
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// insure ForceGuest is disabled for domain controllers.
|
|
//
|
|
|
|
RegStatus = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
if ( RegStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ( (RegValueSize >= sizeof(ULONG)) &&
|
|
(RegValueType == REG_DWORD) )
|
|
{
|
|
if( RegValue == 1 )
|
|
{
|
|
NtLmGlobalForceGuest = TRUE;
|
|
} else {
|
|
NtLmGlobalForceGuest = FALSE;
|
|
}
|
|
}
|
|
} else if ( RegStatus == ERROR_FILE_NOT_FOUND ) {
|
|
|
|
//
|
|
// value was deleted - resort to default.
|
|
//
|
|
|
|
NtLmGlobalForceGuest = FALSE;
|
|
}
|
|
|
|
if ( (!TimedOut) &&
|
|
(PriorForceGuest != NtLmGlobalForceGuest)
|
|
)
|
|
{
|
|
//
|
|
// update targetinfo. the setting changed.
|
|
//
|
|
|
|
RtlAcquireResourceExclusive(&NtLmGlobalCritSect, TRUE);
|
|
|
|
if ( NtLmGlobalNtLm3TargetInfo.Length != 0 )
|
|
{
|
|
SsprUpdateTargetInfo();
|
|
}
|
|
|
|
RtlReleaseResource(&NtLmGlobalCritSect);
|
|
}
|
|
|
|
//
|
|
// handle LimitBlankPasswordUse
|
|
//
|
|
|
|
if ( NtLmGlobalNtProductType != NtProductLanManNt )
|
|
{
|
|
RegValueSize = sizeof( RegValue );
|
|
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"LimitBlankPasswordUse",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR)&RegValue,
|
|
&RegValueSize
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// domain controllers always allow blank.
|
|
//
|
|
|
|
NtLmGlobalAllowBlankPassword = TRUE;
|
|
|
|
RegStatus = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( RegStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ( (RegValueSize >= sizeof(ULONG)) &&
|
|
(RegValueType == REG_DWORD) )
|
|
{
|
|
if ( RegValue == 0 )
|
|
{
|
|
NtLmGlobalAllowBlankPassword = TRUE;
|
|
} else {
|
|
NtLmGlobalAllowBlankPassword = FALSE;
|
|
}
|
|
}
|
|
} else if ( RegStatus == ERROR_FILE_NOT_FOUND ) {
|
|
|
|
//
|
|
// value was deleted - resort to default.
|
|
//
|
|
|
|
NtLmGlobalAllowBlankPassword = FALSE;
|
|
}
|
|
}
|
|
|
|
KeyHandle = NtLmGlobalLsaMsv1_0Key;
|
|
|
|
if ( KeyHandle != NULL )
|
|
{
|
|
//
|
|
// get minimum client security flag.
|
|
//
|
|
|
|
RegValueSize = sizeof( RegValue );
|
|
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"NtlmMinClientSec",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR)&RegValue,
|
|
&RegValueSize
|
|
);
|
|
|
|
|
|
if ( RegStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ((RegValueSize >= sizeof(ULONG)) &&
|
|
(RegValueType == REG_DWORD)) {
|
|
|
|
NtLmGlobalMinimumClientSecurity =
|
|
NtLmValidMinimumSecurityFlagsMask( (ULONG)RegValue );
|
|
}
|
|
} else if ( RegStatus == ERROR_FILE_NOT_FOUND ) {
|
|
|
|
//
|
|
// value was deleted - resort to default.
|
|
//
|
|
|
|
NtLmGlobalMinimumClientSecurity = 0 ;
|
|
}
|
|
|
|
//
|
|
// get minimum server security flags.
|
|
//
|
|
|
|
RegValueSize = sizeof( RegValueSize );
|
|
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"NtlmMinServerSec",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR)&RegValue,
|
|
&RegValueSize
|
|
);
|
|
|
|
|
|
if ( RegStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ((RegValueSize >= sizeof(ULONG)) &&
|
|
(RegValueType == REG_DWORD)) {
|
|
|
|
NtLmGlobalMinimumServerSecurity =
|
|
NtLmValidMinimumSecurityFlagsMask( (ULONG)RegValue );
|
|
}
|
|
} else if ( RegStatus == ERROR_FILE_NOT_FOUND ) {
|
|
|
|
//
|
|
// value was deleted - resort to default.
|
|
//
|
|
|
|
NtLmGlobalMinimumServerSecurity = 0;
|
|
}
|
|
|
|
//
|
|
// All datagram related flags need to be set.
|
|
//
|
|
|
|
if (NtLmGlobalMinimumClientSecurity & NTLMSSP_NEGOTIATE_NTLM2)
|
|
{
|
|
NtLmGlobalRequireNtlm2 = TRUE;
|
|
}
|
|
|
|
if (NtLmGlobalMinimumClientSecurity & NTLMSSP_NEGOTIATE_128)
|
|
{
|
|
NtLmGlobalDatagramUse128BitEncryption = TRUE;
|
|
} else if (NtLmGlobalMinimumClientSecurity & NTLMSSP_NEGOTIATE_56) {
|
|
NtLmGlobalDatagramUse56BitEncryption = TRUE;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// get the debugging flag
|
|
//
|
|
|
|
|
|
RegValueSize = sizeof( RegValueSize );
|
|
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"DBFlag",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR)&RegValue,
|
|
&RegValueSize
|
|
);
|
|
|
|
|
|
if ( RegStatus == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ((RegValueSize >= sizeof(ULONG)) &&
|
|
(RegValueType == REG_DWORD)) {
|
|
|
|
SspGlobalDbflag = (ULONG)RegValue;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// (re)register the wait events.
|
|
//
|
|
|
|
if ( NtLmGlobalRegChangeNotifyEvent && NtLmGlobalLsaKey )
|
|
{
|
|
RegNotifyChangeKeyValue(
|
|
NtLmGlobalLsaKey,
|
|
#if DBG
|
|
TRUE, // catch change events by msv1_0\DBFlag
|
|
#else
|
|
FALSE,
|
|
#endif
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
NtLmGlobalRegChangeNotifyEvent,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
SspPrint((SSP_API, "Leaving NtLmQueryDynamicGlobals\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtLmQueryMappedDomains(
|
|
VOID
|
|
)
|
|
{
|
|
HKEY KeyHandle; // open registry key to Lsa\MSV1_0
|
|
LONG RegStatus;
|
|
DWORD RegValueType;
|
|
WCHAR RegDomainName[DNS_MAX_NAME_LENGTH+1];
|
|
DWORD RegDomainSize;
|
|
|
|
|
|
//
|
|
// register the workitem that waits for the RegChangeNotifyEvent
|
|
// to be signalled. This supports dynamic refresh of configuration
|
|
// parameters.
|
|
//
|
|
|
|
NtLmGlobalRegChangeNotifyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
|
|
//
|
|
// query the globals once prior to registering the wait
|
|
// if a registry change occurs, the globals will be re-read by the worker
|
|
// thread.
|
|
//
|
|
|
|
NtLmQueryDynamicGlobals( NULL, TRUE );
|
|
|
|
NtLmGlobalRegWaitObject = RegisterWaitForSingleObjectEx(
|
|
NtLmGlobalRegChangeNotifyEvent,
|
|
NtLmQueryDynamicGlobals,
|
|
NULL,
|
|
INFINITE,
|
|
0 // dwFlags
|
|
);
|
|
|
|
KeyHandle = NtLmGlobalLsaMsv1_0Key;
|
|
|
|
if ( KeyHandle == NULL )
|
|
return;
|
|
|
|
|
|
//
|
|
// we only support loading the following globals once during initialization;
|
|
// they are not re-read until next reboot.
|
|
//
|
|
|
|
//
|
|
// Check the registry for a domain name to map
|
|
//
|
|
|
|
RegDomainSize = sizeof( RegDomainName );
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"MappedDomain",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR) RegDomainName,
|
|
&RegDomainSize
|
|
);
|
|
|
|
if (RegStatus == ERROR_SUCCESS && RegDomainSize <= 0xFFFF) {
|
|
|
|
NtLmLocklessGlobalMappedDomainString.Length = (USHORT)(RegDomainSize - sizeof(WCHAR));
|
|
NtLmLocklessGlobalMappedDomainString.MaximumLength = (USHORT)RegDomainSize;
|
|
NtLmLocklessGlobalMappedDomainString.Buffer = (PWSTR)NtLmAllocate( RegDomainSize );
|
|
|
|
if( NtLmLocklessGlobalMappedDomainString.Buffer != NULL )
|
|
CopyMemory( NtLmLocklessGlobalMappedDomainString.Buffer,
|
|
RegDomainName,
|
|
RegDomainSize );
|
|
} else {
|
|
RtlInitUnicodeString(
|
|
&NtLmLocklessGlobalMappedDomainString,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Check the registry for a domain name to use
|
|
//
|
|
|
|
RegDomainSize = sizeof( RegDomainName );
|
|
RegStatus = RegQueryValueExW(
|
|
KeyHandle,
|
|
L"PreferredDomain",
|
|
NULL,
|
|
&RegValueType,
|
|
(PUCHAR) RegDomainName,
|
|
&RegDomainSize
|
|
);
|
|
|
|
if (RegStatus == ERROR_SUCCESS && RegDomainSize <= 0xFFFF) {
|
|
|
|
NtLmLocklessGlobalPreferredDomainString.Length = (USHORT)(RegDomainSize - sizeof(WCHAR));
|
|
NtLmLocklessGlobalPreferredDomainString.MaximumLength = (USHORT)RegDomainSize;
|
|
NtLmLocklessGlobalPreferredDomainString.Buffer = (PWSTR)NtLmAllocate( RegDomainSize );
|
|
|
|
if( NtLmLocklessGlobalPreferredDomainString.Buffer != NULL )
|
|
CopyMemory( NtLmLocklessGlobalPreferredDomainString.Buffer,
|
|
RegDomainName,
|
|
RegDomainSize );
|
|
} else {
|
|
RtlInitUnicodeString(
|
|
&NtLmLocklessGlobalPreferredDomainString,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtLmFreeMappedDomains(
|
|
VOID
|
|
)
|
|
{
|
|
if( NtLmGlobalRegWaitObject )
|
|
UnregisterWait( NtLmGlobalRegWaitObject );
|
|
|
|
if( NtLmGlobalRegChangeNotifyEvent )
|
|
CloseHandle( NtLmGlobalRegChangeNotifyEvent );
|
|
|
|
if( NtLmLocklessGlobalMappedDomainString.Buffer ) {
|
|
NtLmFree( NtLmLocklessGlobalMappedDomainString.Buffer );
|
|
NtLmLocklessGlobalMappedDomainString.Buffer = NULL;
|
|
}
|
|
|
|
if( NtLmLocklessGlobalPreferredDomainString.Buffer ) {
|
|
NtLmFree( NtLmLocklessGlobalPreferredDomainString.Buffer );
|
|
NtLmLocklessGlobalPreferredDomainString.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
NtLmCheckProcessOption(
|
|
IN ULONG OptionRequest
|
|
)
|
|
{
|
|
SECPKG_CALL_INFO CallInfo;
|
|
ULONG OptionMask = 0;
|
|
PLIST_ENTRY ListHead;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
if (!LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlAcquireResourceShared( &NtLmProcessOptionsLock, TRUE );
|
|
|
|
ListHead = &NtLmProcessOptionsList;
|
|
|
|
//
|
|
// Now walk the list looking for a match.
|
|
//
|
|
|
|
for ( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
PSSP_PROCESSOPTIONS ProcessOptions;
|
|
|
|
ProcessOptions = CONTAINING_RECORD( ListEntry, SSP_PROCESSOPTIONS, Next );
|
|
|
|
if ( ProcessOptions->ClientProcessID == CallInfo.ProcessId )
|
|
{
|
|
OptionMask = ProcessOptions->ProcessOptions;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlReleaseResource( &NtLmProcessOptionsLock );
|
|
|
|
Cleanup:
|
|
|
|
return OptionMask;
|
|
}
|
|
|
|
BOOLEAN
|
|
NtLmSetProcessOption(
|
|
IN ULONG OptionRequest,
|
|
IN BOOLEAN DisableOption
|
|
)
|
|
{
|
|
SECPKG_CALL_INFO CallInfo;
|
|
PSSP_PROCESSOPTIONS pProcessOption = NULL;
|
|
PLIST_ENTRY ListHead;
|
|
PLIST_ENTRY ListEntry;
|
|
BOOLEAN fExisting = FALSE;
|
|
BOOLEAN fSuccess = FALSE;
|
|
|
|
if (!LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pProcessOption = (PSSP_PROCESSOPTIONS)NtLmAllocate( sizeof(*pProcessOption) );
|
|
if ( pProcessOption == NULL )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pProcessOption->ClientProcessID = CallInfo.ProcessId;
|
|
pProcessOption->ProcessOptions = OptionRequest;
|
|
|
|
RtlAcquireResourceExclusive( &NtLmProcessOptionsLock, TRUE );
|
|
|
|
ListHead = &NtLmProcessOptionsList;
|
|
|
|
//
|
|
// Now walk the list looking for a match.
|
|
//
|
|
|
|
for ( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
PSSP_PROCESSOPTIONS ProcessOptions;
|
|
|
|
ProcessOptions = CONTAINING_RECORD( ListEntry, SSP_PROCESSOPTIONS, Next );
|
|
|
|
if( ProcessOptions->ClientProcessID == CallInfo.ProcessId )
|
|
{
|
|
if( DisableOption )
|
|
{
|
|
ProcessOptions->ProcessOptions &= ~OptionRequest;
|
|
} else {
|
|
ProcessOptions->ProcessOptions |= OptionRequest;
|
|
}
|
|
|
|
fExisting = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !fExisting && !DisableOption )
|
|
{
|
|
InsertHeadList( &NtLmProcessOptionsList, &pProcessOption->Next );
|
|
pProcessOption = NULL;
|
|
}
|
|
|
|
RtlReleaseResource( &NtLmProcessOptionsLock );
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Cleanup:
|
|
|
|
if( pProcessOption != NULL )
|
|
{
|
|
NtLmFree( pProcessOption );
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|