Windows2000/private/security/msv_sspi/ntlm.cxx
2020-09-30 17:12:32 +02:00

992 lines
37 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
// Variables with the EXTERN storage class are declared here
#define NTLM_GLOBAL
#define DEBUG_ALLOCATE
#include <global.h>
// BUGBUG Should be in the SDK?
#define MSV1_0_PACKAGE_NAMEW L"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"
BOOLEAN NtLmCredentialInitialized;
BOOLEAN NtLmContextInitialized;
BOOLEAN NtLmRNGInitialized;
// 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)
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;
*PackageVersion = SECPKG_INTERFACE_VERSION;
*Tables = &NtLmFunctionTable;
*TableCount = 1;
CleanUp:
SspPrint((SSP_API, "Leaving SpLsaModeInitialize\n"));
return(SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR));
}
// Synopsis: Function to be called when policy changes
// 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
CHAR *ComputerNameAnsi = NULL;
CHAR *DomainNameAnsi = NULL;
// Do this only if this is package init
EnterCriticalSection(&NtLmGlobalCritSect);
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
ComputerNameAnsi = NtLmGlobalOemComputerNameString.Buffer;
Status = RtlUpcaseUnicodeStringToOemString(&NtLmGlobalOemComputerNameString,&NtLmGlobalUnicodeComputerNameString,TRUE );
if ( !NT_SUCCESS(Status) ) {
Status = STATUS_SUCCESS;
//SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Error from RtlUpcaseUnicodeStringToOemString is %d\n", Status));
ComputerNameAnsi = 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
DomainNameAnsi = NtLmGlobalOemPrimaryDomainNameString.Buffer;
Status = RtlUpcaseUnicodeStringToOemString(&NtLmGlobalOemPrimaryDomainNameString, &NtLmGlobalUnicodePrimaryDomainNameString, TRUE );
if ( !NT_SUCCESS(Status) ) {
// SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Error from RtlUpcaseUnicodeStringToOemString is %d\n", Status));
DomainNameAnsi = 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;
}
else
{
NtLmGlobalUnicodeTargetName = NtLmGlobalUnicodeComputerNameString;
NtLmGlobalOemTargetName = NtLmGlobalOemComputerNameString;
NtLmGlobalTargetFlags = NTLMSSP_TARGET_TYPE_SERVER;
}
// initialize the GlobalNtlm3 targetinfo.
{
PMSV1_0_AV_PAIR pAV;
PUNICODE_STRING pDnsTargetName;
PUNICODE_STRING pDnsComputerName;
ULONG cbAV;
if( NtLmGlobalNtLm3TargetInfo.Buffer != NULL )
{
NtLmFree(NtLmGlobalNtLm3TargetInfo.Buffer);
}
if( NtLmGlobalTargetFlags == NTLMSSP_TARGET_TYPE_DOMAIN ) {
pDnsTargetName = &NtLmGlobalUnicodeDnsDomainNameString;
} else {
pDnsTargetName = &NtLmGlobalUnicodeDnsComputerNameString;
}
pDnsComputerName = &NtLmGlobalUnicodeDnsComputerNameString;
cbAV = NtLmGlobalUnicodeTargetName.Length +
NtLmGlobalUnicodeComputerNameString.Length +
pDnsComputerName->Length +
pDnsTargetName->Length +
64 ;
NtLmGlobalNtLm3TargetInfo.Buffer = (PWSTR)NtLmAllocate( cbAV );
if( NtLmGlobalNtLm3TargetInfo.Buffer == NULL )
{
Status = STATUS_INSUFFICIENT_RESOURCES;
SspPrint((SSP_CRITICAL, "NtLmSetPolicyInfo, Error from NtLmAllocate\n"));
goto CleanUp;
}
pAV = MsvpAvlInit( NtLmGlobalNtLm3TargetInfo.Buffer );
MsvpAvlAdd( pAV, MsvAvNbDomainName, &NtLmGlobalUnicodeTargetName, cbAV );
MsvpAvlAdd( pAV, MsvAvNbComputerName, &NtLmGlobalUnicodeComputerNameString, cbAV );
if( pDnsTargetName->Length != 0 && pDnsTargetName->Buffer != NULL )
MsvpAvlAdd( pAV, MsvAvDnsDomainName, pDnsTargetName, cbAV );
if( pDnsComputerName->Length != 0 && pDnsComputerName->Buffer != NULL )
MsvpAvlAdd( pAV, MsvAvDnsComputerName, &NtLmGlobalUnicodeDnsComputerNameString, cbAV );
NtLmGlobalNtLm3TargetInfo.Length = (USHORT)MsvpAvlLen( pAV, cbAV );
}
CleanUp:
LeaveCriticalSection(&NtLmGlobalCritSect);
if (ComputerNameAnsi)
{
NtLmFree(ComputerNameAnsi);
}
if (DomainNameAnsi)
{
NtLmFree(DomainNameAnsi);
}
return Status;
}
// Synopsis: Function to be called when domain policy changes
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;
}
}
break;
default:
break;
}
Cleanup:
if (Policy != NULL)
{
switch (ChangedInfoClass)
{
case PolicyNotifyDnsDomainInformation:
{
I_LsaIFree_LSAPR_POLICY_INFORMATION(PolicyDnsDomainInformation,Policy);
}
break;
default:
break;
}
}
}
// Synopsis: Register with the LSA to be notified of policy changes
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);
}
// Synopsis: Unregister for policy change notification
VOID NtLmUnregisterForPolicyChange(IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass)
{
(VOID) I_LsaIUnregisterPolicyChangeNotificationCallback(NtLmPolicyChangeCallback, ChangedInfoClass);
}
// 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.(BUGBUG verify)
// 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
InitializeCriticalSection(&NtLmGlobalCritSect);
// 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"));
}
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';
}
// 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;
}
// 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 ) ) {
if(RtlEqualLuid(&LogonIdSystem,&(LocalTokenStatistics.AuthenticationId))) {// see if it's SYSTEM.
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;
if( !NtLmInitializeProtectedMemory() ) {
SspPrint((SSP_CRITICAL, "SpInitialize, Error from NtLmInitializeProtectedMemory\n"));
Status = STATUS_UNSUCCESSFUL;
goto CleanUp;
}
NtLmCheckLmCompatibility();
NtLmQueryMappedDomains();
// 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));
}
// 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 (NtLmRNGInitialized)
{
NtLmCleanupRNG();
NtLmRNGInitialized = FALSE;
}
NtLmFreeMappedDomains();
NtLmUnregisterForPolicyChange(PolicyNotifyDnsDomainInformation);
if (NtLmGlobalAccessTokenSystem != NULL) {
NtClose( NtLmGlobalAccessTokenSystem );
NtLmGlobalAccessTokenSystem = NULL;
}
DeleteCriticalSection(&NtLmGlobalCritSect);
SspPrint((SSP_API, "Leaving SpShutdown\n"));
#if DBG
DeleteCriticalSection(&SspGlobalLogFileCritSect);
#endif
#endif // NTBUG 400026
NtLmCleanupProtectedMemory();
return(SEC_E_OK);
}
// 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"));
// BUGBUG Do we remove the PRIVACY flags if Encryption is not enabled?
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)
{
return SEC_E_UNSUPPORTED_FUNCTION ;
}
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
--*/
{
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;
}
}
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"));
}
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);
}
}
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;
}
}