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