Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2480 lines
71 KiB

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