2090 lines
55 KiB
C++
2090 lines
55 KiB
C++
//+--------------------------------------------------------------------
|
||
//
|
||
// 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"
|
||
{
|
||
|
||
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: 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;
|
||
|
||
|
||
//
|
||
// 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) ) {
|
||
Status = STATUS_SUCCESS;
|
||
//SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Error from RtlUpcaseUnicodeStringToOemString is %d\n", Status));
|
||
ComputerNameAnsiString.Buffer = NULL;
|
||
// 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) ) {
|
||
// SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Error from RtlUpcaseUnicodeStringToOemString is %d\n", Status));
|
||
DomainNameAnsiString.Buffer = NULL;
|
||
// goto CleanUp;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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();
|
||
|
||
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;
|
||
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
|
||
//
|
||
// Init the global crit section
|
||
//
|
||
|
||
RtlInitializeResource(&NtLmGlobalCritSect);
|
||
|
||
RtlInitializeResource(&NtLmProcessOptionsLock);
|
||
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;
|
||
|
||
(VOID)AllocateAndInitializeSid(
|
||
&siaNtAuthority,
|
||
1,
|
||
SECURITY_ANONYMOUS_LOGON_RID,
|
||
0, 0, 0, 0, 0, 0, 0,
|
||
&NtLmGlobalAnonymousSid
|
||
);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// pickup a copy of the Local System access token.
|
||
//
|
||
|
||
{
|
||
HANDLE hProcessToken;
|
||
NTSTATUS StatusToken;
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
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(
|
||
)
|
||
/*++
|
||
|
||
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;
|
||
|
||
|
||
//
|
||
// 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 f
|
||
)
|
||
{
|
||
SspPrint((SSP_API, "Entering NtLmQueryDynamicGlobals\n"));
|
||
|
||
HKEY KeyHandle; // open registry key to Lsa\MSV1_0
|
||
LONG RegStatus;
|
||
|
||
DWORD RegValueType;
|
||
DWORD RegValue;
|
||
DWORD RegValueSize;
|
||
|
||
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
|
||
//
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// 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) &&
|
||
(NtLmSecPkg.MachineState & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED))
|
||
{
|
||
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 )
|
||
{
|
||
if( NtLmGlobalLsaKey )
|
||
{
|
||
RegNotifyChangeKeyValue(
|
||
NtLmGlobalLsaKey,
|
||
FALSE,
|
||
REG_NOTIFY_CHANGE_LAST_SET,
|
||
NtLmGlobalRegChangeNotifyEvent,
|
||
TRUE
|
||
);
|
||
}
|
||
|
||
#if DBG
|
||
if( NtLmGlobalLsaMsv1_0Key )
|
||
{
|
||
RegNotifyChangeKeyValue(
|
||
NtLmGlobalLsaMsv1_0Key,
|
||
FALSE,
|
||
REG_NOTIFY_CHANGE_LAST_SET,
|
||
NtLmGlobalRegChangeNotifyEvent,
|
||
TRUE
|
||
);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
|
||
|
||
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, FALSE );
|
||
|
||
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;
|
||
}
|