|
|
//+--------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: ntdigest.c
//
// Contents: main entrypoints for the digest security package
// SpLsaModeInitialize
// SpInitialize
// SpShutdown
// SpGetInfo
//
// Helper functions:
//
// History: KDamour 10Mar00 Stolen from msv_sspi\ntlm.cxx
//
//---------------------------------------------------------------------
#define NTDIGEST_GLOBAL
#include "global.h"
/* Debugging information setup */ DEFINE_DEBUG2(Digest);
DEBUG_KEY MyDebugKeys[] = {{DEB_ERROR, "Error"}, {DEB_WARN, "Warning"}, {DEB_TRACE, "Trace"}, {DEB_TRACE_ASC, "TraceASC"}, {DEB_TRACE_ISC, "TraceISC"}, {DEB_TRACE_LSA, "TraceLSA"}, {DEB_TRACE_USER, "TraceUser"}, {DEB_TRACE_FUNC, "TraceFuncs"}, {DEB_TRACE_MEM, "TraceMem"}, {TRACE_STUFF, "Stuff"}, {0, NULL} };
// set to TRUE once initialized
BOOL l_bDebugInitialized = FALSE; BOOL l_bDigestInitialized = FALSE;
// Registry reading
HKEY g_hkBase = NULL; HANDLE g_hParamEvent = NULL; HANDLE g_hWait = NULL;
#define COMPUTER_NAME_SIZE (MAX_COMPUTERNAME_LENGTH + 1)
//+--------------------------------------------------------------------
//
// 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
DebugInitialize(); #endif
DebugLog((DEB_TRACE_FUNC, "SpLsaModeInitialize: Entering\n"));
SECURITY_STATUS Status = SEC_E_OK;
if (LsaVersion != SECPKG_INTERFACE_VERSION) { DebugLog((DEB_ERROR, "SpLsaModeInitialize: Invalid LSA version: %d\n", LsaVersion)); Status = STATUS_INVALID_PARAMETER; goto CleanUp; }
// Fill in the dispatch table for functions exported by ssp
g_NtDigestFunctionTable.InitializePackage = NULL; g_NtDigestFunctionTable.LogonUser = NULL; g_NtDigestFunctionTable.CallPackage = LsaApCallPackage; g_NtDigestFunctionTable.LogonTerminated = LsaApLogonTerminated; g_NtDigestFunctionTable.CallPackageUntrusted = LsaApCallPackageUntrusted; g_NtDigestFunctionTable.LogonUserEx = NULL; g_NtDigestFunctionTable.LogonUserEx2 = LsaApLogonUserEx2; g_NtDigestFunctionTable.Initialize = SpInitialize; g_NtDigestFunctionTable.Shutdown = SpShutdown; g_NtDigestFunctionTable.GetInfo = SpGetInfo; g_NtDigestFunctionTable.AcceptCredentials = SpAcceptCredentials; g_NtDigestFunctionTable.AcquireCredentialsHandle = SpAcquireCredentialsHandle; g_NtDigestFunctionTable.FreeCredentialsHandle = SpFreeCredentialsHandle; g_NtDigestFunctionTable.SaveCredentials = SpSaveCredentials; g_NtDigestFunctionTable.GetCredentials = SpGetCredentials; g_NtDigestFunctionTable.DeleteCredentials = SpDeleteCredentials; g_NtDigestFunctionTable.InitLsaModeContext = SpInitLsaModeContext; g_NtDigestFunctionTable.AcceptLsaModeContext = SpAcceptLsaModeContext; g_NtDigestFunctionTable.DeleteContext = SpDeleteContext; g_NtDigestFunctionTable.ApplyControlToken = SpApplyControlToken; g_NtDigestFunctionTable.GetUserInfo = SpGetUserInfo; g_NtDigestFunctionTable.QueryCredentialsAttributes = SpQueryCredentialsAttributes ; g_NtDigestFunctionTable.GetExtendedInformation = SpGetExtendedInformation ; g_NtDigestFunctionTable.SetExtendedInformation = SpSetExtendedInformation ; g_NtDigestFunctionTable.CallPackagePassthrough = LsaApCallPackagePassthrough;
*PackageVersion = SECPKG_INTERFACE_VERSION; *Tables = &g_NtDigestFunctionTable; *TableCount = 1;
CleanUp:
DebugLog((DEB_TRACE_FUNC, "SpLsaModeInitialize:Leaving\n"));
return(Status); }
//+--------------------------------------------------------------------
//
// 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.(BUGBUG verify)
//
//---------------------------------------------------------------------
NTSTATUS NTAPI SpInitialize( IN ULONG_PTR pPackageId, IN PSECPKG_PARAMETERS pParameters, IN PLSA_SECPKG_FUNCTION_TABLE pFunctionTable ) {
SECURITY_STATUS Status = SEC_E_OK; DWORD dwWinErr = 0; NT_PRODUCT_TYPE NtProductType = NtProductWinNt; WCHAR wszComputerName[COMPUTER_NAME_SIZE]; DWORD dwComputerNameLen = COMPUTER_NAME_SIZE;
DebugLog((DEB_TRACE_FUNC, "SpInitialize: Entering\n"));
// Indicate that we completed initialization
ASSERT(l_bDigestInitialized == FALSE); // never called more than once
l_bDigestInitialized = TRUE;
// Initialize global values
ZeroMemory(&g_strNtDigestUTF8ServerRealm, sizeof(g_strNtDigestUTF8ServerRealm)); ZeroMemory(&g_strNTDigestISO8859ServerRealm, sizeof(g_strNTDigestISO8859ServerRealm));
// Define time for AcquirCredentialHandle
// 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.
g_TimeForever.HighPart = 0x7FFFFFFF; g_TimeForever.LowPart = 0xFFFFFFFF;
//
// All the following are global
//
g_NtDigestState = NtDigestLsaMode; /* enum */ g_NtDigestPackageId = pPackageId;
//
// Save away the Lsa functions
//
g_LsaFunctions = pFunctionTable;
//
// Establish the packagename
//
RtlInitUnicodeString( &g_ustrNtDigestPackageName, WDIGEST_SP_NAME );
// Set the WorkstationName
if (!GetComputerNameExW(ComputerNameNetBIOS, wszComputerName, &dwComputerNameLen)) { ZeroMemory(&g_ustrWorkstationName, sizeof(g_ustrWorkstationName)); DebugLog((DEB_ERROR, "SpInitialize: Get ComputerName error 0x%x\n", GetLastError())); } else { Status = UnicodeStringWCharDuplicate(&g_ustrWorkstationName, wszComputerName); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: ComputerName copy status 0x%x\n", Status)); goto CleanUp; } }
// Need to initialize Crypto stuff and nonce creations
Status = NonceInitialize(); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from NonceInitialize status 0x%x\n", Status)); goto CleanUp; }
//
// Determine if this machine is running Windows NT or Lanman NT.
// LanMan NT runs on a domain controller.
//
if ( !RtlGetNtProductType( &NtProductType ) ) { // Nt Product Type undefined - WinNt assumed
NtProductType = NtProductWinNt; }
if (NtProductType == NtProductLanManNt) { g_fDomainController = TRUE; // Allow password checking only on DomainControllers
}
//
// Save the Parameters info to a global struct
//
g_NtDigestSecPkg.MachineState = pParameters->MachineState; g_NtDigestSecPkg.SetupMode = pParameters->SetupMode; g_NtDigestSecPkg.Version = pParameters->Version;
Status = UnicodeStringDuplicate( &g_NtDigestSecPkg.DnsDomainName, &(pParameters->DnsDomainName)); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from UnicodeStringDuplicate status 0x%x\n", Status)); goto CleanUp; }
Status = UnicodeStringDuplicate( &g_NtDigestSecPkg.DomainName, &(pParameters->DomainName)); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from UnicodeStringDuplicate status 0x%x\n", Status)); goto CleanUp; }
if (pParameters->DomainSid != NULL) { Status = SidDuplicate( &g_NtDigestSecPkg.DomainSid, pParameters->DomainSid ); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from SidDuplicate status 0x%x\n", Status)); goto CleanUp; } } else g_NtDigestSecPkg.DomainSid = NULL;
DebugLog((DEB_TRACE, "SpInitialize: DNSDomain = %wZ, Domain = %wZ\n", &(g_NtDigestSecPkg.DnsDomainName), &(g_NtDigestSecPkg.DomainName)));
// For server challenges, precalculate the UTF-8 and ISO versions of the realm
Status = EncodeUnicodeString(&(g_NtDigestSecPkg.DnsDomainName), CP_8859_1, &g_strNTDigestISO8859ServerRealm, NULL); if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "SpInitialize: Error in encoding domain in ISO-8859-1\n")); ZeroMemory(&g_strNTDigestISO8859ServerRealm, sizeof(STRING)); }
Status = EncodeUnicodeString(&(g_NtDigestSecPkg.DnsDomainName), CP_UTF8, &g_strNtDigestUTF8ServerRealm, NULL); if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "SpInitialize: Error in encoding domain in UTF-8\n")); ZeroMemory(&g_strNtDigestUTF8ServerRealm, sizeof(STRING)); }
//
// Initialize the digest token source
//
RtlCopyMemory( g_DigestSource.SourceName, NTDIGEST_TOKEN_NAME_A, sizeof(NTDIGEST_TOKEN_NAME_A) );
NtAllocateLocallyUniqueId(&g_DigestSource.SourceIdentifier);
//
// Init the LogonSession stuff
//
Status = LogSessHandlerInit(); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from LogSessHandlerInit status 0x%x\n", Status)); goto CleanUp; }
//
// Init the Credential stuff
//
Status = CredHandlerInit(); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from CredHandlerInit status 0x%x\n", Status)); goto CleanUp; }
//
// Init the Context stuff
//
Status = CtxtHandlerInit(); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInitialize: Error from ContextInitialize status 0x%x\n", Status)); goto CleanUp; }
//
// Read in the registry values for SSP configuration - in LSA space
//
SPLoadRegOptions();
CleanUp:
if (!NT_SUCCESS (Status)) { SPUnloadRegOptions(); SpShutdown(); }
DebugLog((DEB_TRACE_FUNC, "SpInitialize: Leaving\n"));
return(Status); }
//+--------------------------------------------------------------------
//
// 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 ) { DebugLog((DEB_TRACE_FUNC, "SpShutdown: Entering\n"));
// Need to identify how to shutdown without causing faults with
// incoming messages
DebugLog((DEB_TRACE_FUNC, "SpShutdown: Leaving\n"));
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. This function required
// to return SUCCESS for the package to stay loaded.
//
//---------------------------------------------------------------------
NTSTATUS NTAPI SpGetInfo( OUT PSecPkgInfo PackageInfo ) { DebugLog((DEB_TRACE_FUNC, "SpGetInfo: Entering\n"));
PackageInfo->fCapabilities = NTDIGEST_SP_CAPS; PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; PackageInfo->wRPCID = RPC_C_AUTHN_DIGEST; PackageInfo->cbMaxToken = NTDIGEST_SP_MAX_TOKEN_SIZE; PackageInfo->Name = WDIGEST_SP_NAME; PackageInfo->Comment = NTDIGEST_SP_COMMENT;
DebugLog((DEB_TRACE_FUNC, "SpGetInfo: Leaving\n"));
return(SEC_E_OK); }
// Misc SECPKG Functions
NTSTATUS NTAPI SpGetUserInfo( IN PLUID LogonId, IN ULONG Flags, OUT PSecurityUserData * UserData ) { DebugLog((DEB_TRACE_FUNC, "SpGetUserInfo: Entering/Leaving\n"));
// FIXIFX Fields of UserData are username, domain, server
UNREFERENCED_PARAMETER(LogonId); UNREFERENCED_PARAMETER(Flags); UNREFERENCED_PARAMETER(UserData);
return(SEC_E_UNSUPPORTED_FUNCTION); }
//+---------------------------------------------------------------------------
//
// Function: SpGetExtendedInformation
//
// Synopsis: Return extended information to the LSA
//
// Arguments: [Class] -- Information Class
// [pInfo] -- Returned Information Pointer
//
//
// Notes:
//
//----------------------------------------------------------------------------
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 = 0;
DebugLog((DEB_TRACE_FUNC, "SpGetExtendedInformation: Entering\n"));
switch ( Class ) {
case SecpkgWowClientDll:
//
// This indicates that we're smart enough to handle wow client processes
//
Information = (PSECPKG_EXTENDED_INFORMATION) DigestAllocateMemory( 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"\\" NTDIGEST_DLL_NAME, 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 ) { DigestFreeMemory( Information ); }
DebugLog((DEB_TRACE_FUNC, "SpGetExtendedInformation: Leaving Status %d\n", Status));
return Status ; }
NTSTATUS NTAPI SpSetExtendedInformation( IN SECPKG_EXTENDED_INFORMATION_CLASS Class, IN PSECPKG_EXTENDED_INFORMATION Info ) { DebugLog((DEB_TRACE_FUNC, "SpSetExtendedInformation: Entering/Leaving \n"));
UNREFERENCED_PARAMETER(Class); UNREFERENCED_PARAMETER(Info); return(SEC_E_UNSUPPORTED_FUNCTION) ; }
//
// Registry Reading routines
// This routine is called in single-threaded mode from the LSA for SpInitialize and SPInstanceInit
// In user applications only SPInstanceInit calls this function
//
BOOL SPLoadRegOptions(void) { if (NULL != g_hParamEvent) { // Already called - no need to re-execute
DebugLog((DEB_TRACE, "SPLoadRegOptions: Already initialized - Leaving \n")); return TRUE; }
g_hParamEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
DigestWatchParamKey(g_hParamEvent, FALSE);
return TRUE; }
void SPUnloadRegOptions(void) { if (NULL != g_hWait) { RtlDeregisterWaitEx(g_hWait, (HANDLE)-1); g_hWait = NULL; }
if(NULL != g_hkBase) { RegCloseKey(g_hkBase); g_hkBase = NULL; }
if(NULL != g_hParamEvent) { CloseHandle(g_hParamEvent); g_hParamEvent = NULL; }
}
// Helper function to read in a DWORD - sets value if not present in registry
void ReadDwordRegistrySetting( HKEY hReadKey, HKEY hWriteKey, LPCTSTR pszValueName, DWORD * pdwValue, DWORD dwDefaultValue) { DWORD dwSize = 0; DWORD dwType = 0;
dwSize = sizeof(DWORD); if(RegQueryValueEx(hReadKey, pszValueName, NULL, &dwType, (PUCHAR)pdwValue, &dwSize) != STATUS_SUCCESS) { *pdwValue = dwDefaultValue;
if(hWriteKey) { RegSetValueEx(hWriteKey, pszValueName, 0, REG_DWORD, (PUCHAR)pdwValue, sizeof(DWORD)); } } }
// Can be called at any time to change the default values
// As long as a DWORD assignment can be done in a single step
BOOL NtDigestReadRegistry(BOOL fFirstTime) { DWORD dwBool = 0; DWORD dwDebug = 0;
HKEY hWriteKey = 0;
// Open top-level key that has write access.
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_DIGEST_BASE, 0, KEY_READ | KEY_SET_VALUE, &hWriteKey) != STATUS_SUCCESS) { hWriteKey = 0; }
// "LifeTime"
ReadDwordRegistrySetting( g_hkBase, hWriteKey, REG_DIGEST_OPT_LIFETIME, &g_dwParameter_Lifetime, PARAMETER_LIFETIME);
// "Negotiate" Supported - BOOL value
ReadDwordRegistrySetting( g_hkBase, hWriteKey, REG_DIGEST_OPT_NEGOTIATE, &dwBool, PARAMETER_NEGOTIATE); if (dwBool) g_fParameter_Negotiate = TRUE; else g_fParameter_Negotiate = FALSE;
// UTF8 Supported in HTTP mode - BOOL value
ReadDwordRegistrySetting( g_hkBase, hWriteKey, REG_DIGEST_OPT_UTF8HTTP, &dwBool, PARAMETER_UTF8_HTTP); if (dwBool) g_fParameter_UTF8HTTP = TRUE; else g_fParameter_UTF8HTTP = FALSE;
// UTF8 supported in SASL - BOOL value
ReadDwordRegistrySetting( g_hkBase, hWriteKey, REG_DIGEST_OPT_UTF8SASL, &dwBool, PARAMETER_UTF8_SASL); if (dwBool) g_fParameter_UTF8SASL = TRUE; else g_fParameter_UTF8SASL = FALSE;
// MaxContextCount
/*
ReadDwordRegistrySetting( g_hkBase, hWriteKey, REG_DIGEST_OPT_MAXCTXTCOUNT, &g_dwParameter_MaxCtxtCount, PARAMETER_MAXCTXTCOUNT); */
#if DBG
// DebugLevel
ReadDwordRegistrySetting( g_hkBase, hWriteKey, REG_DIGEST_OPT_DEBUGLEVEL, &dwDebug, 0); DigestInfoLevel = dwDebug; // Turn on/off selected messages
#endif
if(hWriteKey) { RegCloseKey(hWriteKey); hWriteKey = 0; }
DebugLog((DEB_TRACE, "NtDigestReadRegistry: Lifetime %lu, Negotiate %d, UTF-8 HTTP %d, UTF-8 SASL %d, DebugLevel 0x%x\n", g_dwParameter_Lifetime, g_fParameter_Negotiate, g_fParameter_UTF8HTTP, g_fParameter_UTF8SASL, dwDebug));
return TRUE; }
// This routine is called in single-threaded mode from the LSA for SpLsaModeInitialize and SPInstanceInit
// In user applications only SPInstanceInit calls this function
void DebugInitialize(void) { #if DBG
if (l_bDebugInitialized == TRUE) { return; } l_bDebugInitialized = TRUE; DigestInitDebug(MyDebugKeys); DigestInfoLevel = 0x0; // Turn on OFF messages - Registry read will adjust which ones to keep on
#endif
return; }
////////////////////////////////////////////////////////////////////
//
// Name: DigestWatchParamKey
//
// Synopsis: Sets RegNotifyChangeKeyValue() on param key, initializes
// debug level, then utilizes thread pool to wait on
// changes to this registry key. Enables dynamic debug
// level changes, as this function will also be callback
// if registry key modified.
//
// Arguments: pCtxt is actually a HANDLE to an event. This event
// will be triggered when key is modified.
//
// Notes: .
//
VOID DigestWatchParamKey( PVOID pCtxt, BOOLEAN fWaitStatus) { NTSTATUS Status; LONG lRes = ERROR_SUCCESS; BOOL fFirstTime = FALSE; DWORD disp;
if(g_hkBase == NULL) { // First time we've been called.
Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, REG_DIGEST_BASE, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &g_hkBase, &disp); if(Status) { DebugLog((DEB_WARN,"Failed to open WDigest key: 0x%x\n", Status)); return; }
fFirstTime = TRUE; }
if(pCtxt != NULL) { if (NULL != g_hWait) { Status = RtlDeregisterWait(g_hWait); if(!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status)); goto Reregister; } }
lRes = RegNotifyChangeKeyValue( g_hkBase, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, (HANDLE)pCtxt, TRUE);
if (ERROR_SUCCESS != lRes) { DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes)); // we're tanked now. No further notifications, so get this one
} }
NtDigestReadRegistry(fFirstTime);
Reregister:
if(pCtxt != NULL) { Status = RtlRegisterWait(&g_hWait, (HANDLE)pCtxt, DigestWatchParamKey, (HANDLE)pCtxt, INFINITE, WT_EXECUTEINPERSISTENTIOTHREAD| WT_EXECUTEONLYONCE); } }
|