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