windows-nt/Source/XPSP1/NT/ds/security/gina/msgina/domain.c
2020-09-26 16:20:57 +08:00

2368 lines
50 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: domcache.c
//
// Contents: Restructuring the Domain Cache to get away from direct LSA
// calls whenever possible.
//
// Classes:
//
// Functions:
//
// History: 3-29-96 RichardW Created
//
//----------------------------------------------------------------------------
#include <msgina.h>
#include <stdio.h>
#define LockDomainCache( x ) RtlEnterCriticalSection( &(x)->CriticalSection )
#define UnlockDomainCache( x ) RtlLeaveCriticalSection( &(x)->CriticalSection )
#define TWO_MINUTES ((LONGLONG) 0x47868C00)
#define TWO_WEEKS ((LONGLONG) 0xB0051C88000I64)
WCHAR szCache[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\DomainCache");
WCHAR szCacheValue[] = TEXT("DCache");
WCHAR szCacheUpdate[] = TEXT("DCacheUpdate");
WCHAR szCacheInterval[] = TEXT("DCacheMinInterval");
WCHAR szCachePrimary[] = TEXT("CachePrimaryDomain");
LONGLONG CacheUpdateMin;
LONGLONG CacheUpdateMax;
BOOL CacheAppendDomainInfo = FALSE ;
BOOL CacheShowDnsNames = FALSE ;
#define DOMAIN_MOD_ALWAYS 0x00000001
typedef struct _DOMAIN_MODIFIER {
ULONG StringId ;
ULONG Flags ;
UNICODE_STRING String ;
} DOMAIN_MODIFIER ;
DOMAIN_MODIFIER CacheDomainModifiers[ DomainTypeMax ] = {
{ 0 }, // Invalid
{ IDS_DTYPE_UPNDOMAIN, 0 }, // UPN Domain
{ IDS_DTYPE_THISCOMPUTER, DOMAIN_MOD_ALWAYS }, // This computer
{ IDS_DTYPE_NT4DOMAIN, 0 }, // NT4 domains
{ IDS_DTYPE_NT5DOMAIN, 0 }, // NT5 domains
{ IDS_DTYPE_MITDOMAIN, DOMAIN_MOD_ALWAYS }, // MIT domains
{ IDS_DTYPE_MITXDOMAIN, DOMAIN_MOD_ALWAYS }, // Untrusted MIT domains
{ IDS_DTYPE_NETPROVIDER, DOMAIN_MOD_ALWAYS } // Network provider
};
DWORD
DCacheUpdateThread(
PDOMAIN_CACHE Cache
);
VOID
DCacheDereferenceEntry(
PDOMAIN_CACHE_ENTRY Entry
)
{
if ( InterlockedDecrement( &Entry->RefCount ) == 0 )
{
LocalFree( Entry );
}
}
PDOMAIN_CACHE_ARRAY
DCacheCreateArray(
ULONG Size,
BOOL Sorted
)
{
PDOMAIN_CACHE_ARRAY Array ;
Array = LocalAlloc( LMEM_FIXED, sizeof( DOMAIN_CACHE_ARRAY ) );
if ( Array )
{
Array->List = LocalAlloc( LMEM_FIXED, sizeof( PDOMAIN_CACHE_ENTRY ) * Size );
if ( Array->List )
{
Array->Count = 0 ;
Array->MaxCount = Size ;
Array->Sorted = Sorted ;
return Array ;
}
LocalFree( Array );
}
return NULL ;
}
BOOL
DCachepExpandArray(
PDOMAIN_CACHE_ARRAY Array,
ULONG Size
)
{
PDOMAIN_CACHE_ENTRY * NewArray ;
NewArray = LocalReAlloc( Array->List,
(Array->Count + Size) * sizeof( PDOMAIN_CACHE_ENTRY ),
0 );
if ( NewArray )
{
Array->List = NewArray ;
Array->MaxCount = Array->Count + Size ;
return TRUE ;
}
return FALSE ;
}
BOOL
DCacheInsertArray(
PDOMAIN_CACHE_ARRAY Array,
PDOMAIN_CACHE_ENTRY Entry
)
{
ULONG i ;
LONG Compare ;
PDOMAIN_CACHE_ENTRY Scan ;
if ( Array->Count == Array->MaxCount )
{
if ( !DCachepExpandArray( Array, 10 ) )
{
return FALSE ;
}
}
DCacheReferenceEntry( Entry );
if ( ( Array->Sorted == FALSE ) ||
( Array->Count == 0 ) )
{
Array->List[ Array->Count ] = Entry ;
Array->Count++ ;
return TRUE ;
}
else
{
Scan = Array->List[ Array->Count - 1 ];
Compare = RtlCompareUnicodeString( &Entry->FlatName,
&Scan->FlatName,
TRUE );
//
// Efficient check for sorted input:
//
if ( Compare > 0 )
{
Array->List[ Array->Count ] = Entry ;
Array->Count ++ ;
return TRUE ;
}
//
// this is not a terribly efficient sort.
// However, we're expecting
// on the order of <100 objects in the array, so it
// shouldn't be too bad.
//
for ( i = 0 ; i < Array->Count ; i++ )
{
Scan = Array->List[ i ];
Compare = RtlCompareUnicodeString( & Entry->FlatName,
& Scan->FlatName,
TRUE );
if ( Compare == 0 )
{
DCacheDereferenceEntry( Entry );
return FALSE ;
}
if ( Compare < 0 )
{
break;
}
}
RtlMoveMemory(
&Array->List[ i + 1 ],
&Array->List[ i ],
(Array->Count - i) * sizeof( PVOID ) );
Array->List[ i ] = Entry ;
Array->Count++ ;
return TRUE ;
}
}
PDOMAIN_CACHE_ENTRY
DCacheSearchArray(
PDOMAIN_CACHE_ARRAY Array,
PUNICODE_STRING DomainName
)
{
ULONG i ;
PDOMAIN_CACHE_ENTRY Scan = NULL ;
LONG Compare ;
for (i = 0 ; i < Array->Count ; i++ )
{
Scan = Array->List[ i ];
if ( Scan->FlatName.Length == 0 )
{
Scan = NULL ;
continue;
}
Compare = RtlCompareUnicodeString( &Scan->FlatName,
DomainName,
TRUE );
if ( Compare == 0 )
{
break;
}
if ( ( Compare > 0 ) &&
( Array->Sorted ) )
{
Scan = NULL ;
break;
}
}
return Scan ;
}
PDOMAIN_CACHE_ENTRY
DCacheSearchArrayByDns(
PDOMAIN_CACHE_ARRAY Array,
PUNICODE_STRING DnsDomainName
)
{
ULONG i ;
PDOMAIN_CACHE_ENTRY Scan = NULL ;
for (i = 0 ; i < Array->Count ; i++ )
{
Scan = Array->List[ i ];
if ( Scan->DnsName.Length )
{
if ( RtlEqualUnicodeString( &Scan->DnsName,
DnsDomainName,
TRUE ) )
{
break;
}
}
Scan = NULL ;
}
return Scan ;
}
PDOMAIN_CACHE_ENTRY
DCacheFindDefaultEntry(
PDOMAIN_CACHE_ARRAY Array
)
{
ULONG i ;
PDOMAIN_CACHE_ENTRY Scan = NULL ;
for (i = 0 ; i < Array->Count ; i++ )
{
Scan = Array->List[ i ];
if ( Scan->Flags & DCE_DEFAULT_ENTRY )
{
break;
}
Scan = NULL ;
}
return Scan ;
}
VOID
DCacheFreeArray(
PDOMAIN_CACHE_ARRAY Array
)
{
ULONG i ;
for ( i = 0 ; i < Array->Count ; i++ )
{
DCacheDereferenceEntry( Array->List[ i ] );
}
LocalFree( Array->List );
LocalFree( Array );
}
PDOMAIN_CACHE_ARRAY
DCacheCopyArray(
PDOMAIN_CACHE_ARRAY Source
)
{
PDOMAIN_CACHE_ARRAY Array ;
ULONG i ;
Array = DCacheCreateArray( Source->MaxCount,
Source->Sorted );
if ( Array )
{
for (i = 0 ; i < Source->Count ; i++ )
{
Array->List[ i ] = Source->List[ i ];
DCacheReferenceEntry( Array->List[ i ] );
}
Array->Count = Source->Count ;
}
return Array ;
}
PDOMAIN_CACHE_ENTRY
DCacheCreateEntry(
DOMAIN_ENTRY_TYPE Type,
PUNICODE_STRING FlatName OPTIONAL,
PUNICODE_STRING DnsName OPTIONAL,
PUNICODE_STRING DisplayName OPTIONAL
)
{
ULONG Size ;
PDOMAIN_CACHE_ENTRY Entry ;
PUNICODE_STRING DisplayBase = NULL ;
PUNICODE_STRING Modifier = NULL ;
PUCHAR Current ;
//
// Validation rules:
//
// Display Name is optional if either of FlatName or
// DNS name is present. If both are present, FlatName
// is defaulted over Dns name
//
Size = sizeof( DOMAIN_CACHE_ENTRY );
if ( FlatName )
{
Size += FlatName->Length + sizeof( WCHAR );
}
if ( DnsName )
{
Size += DnsName->Length + sizeof( WCHAR );
}
if ( DisplayName )
{
Size += DisplayName->Length + sizeof( WCHAR );
DisplayBase = DisplayName ;
}
else
{
if ( CacheShowDnsNames ||
( ( Type == DomainMitRealm ) ||
( Type == DomainMitUntrusted ) ) )
{
if ( DnsName )
{
DisplayBase = DnsName ;
}
else if ( FlatName )
{
DisplayBase = FlatName ;
}
else
{
return NULL ;
}
}
else
{
if ( FlatName )
{
DisplayBase = FlatName ;
}
else if ( DnsName )
{
DisplayBase = DnsName ;
}
else
{
return NULL ;
}
}
Size += DisplayBase->Length + sizeof( WCHAR );
if ( ( CacheAppendDomainInfo ) ||
( CacheDomainModifiers[ Type ].Flags & DOMAIN_MOD_ALWAYS ) )
{
Modifier = &CacheDomainModifiers[ Type ].String ;
if ( Modifier->Length )
{
Size += CacheDomainModifiers[ Type ].String.Length;
}
else
{
Modifier = NULL ;
}
}
}
Entry = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Size );
if ( !Entry )
{
return NULL ;
}
Entry->RefCount = 1 ;
Entry->Flags = 0 ;
Entry->Type = Type ;
Current = (PUCHAR) ( Entry + 1 );
//
// Copy and pack the strings:
//
if ( FlatName )
{
Entry->FlatName.Buffer = (PWSTR) Current ;
Entry->FlatName.Length = FlatName->Length ;
Entry->FlatName.MaximumLength = FlatName->Length + sizeof( WCHAR );
RtlCopyMemory(
Current,
FlatName->Buffer,
FlatName->Length );
Current += FlatName->Length ;
*Current++ = '\0';
*Current++ = '\0';
}
if ( DnsName )
{
Entry->DnsName.Buffer = (PWSTR) Current ;
Entry->DnsName.Length = DnsName->Length ;
Entry->DnsName.MaximumLength = DnsName->Length + sizeof( WCHAR );
RtlCopyMemory(
Current,
DnsName->Buffer,
DnsName->Length );
Current += DnsName->Length ;
*Current++ = '\0';
*Current++ = '\0';
}
ASSERT( DisplayBase );
Entry->DisplayName.Buffer = (PWSTR) Current ;
Entry->DisplayName.Length = DisplayBase->Length ;
if ( Modifier )
{
Entry->DisplayName.Length = Entry->DisplayName.Length + Modifier->Length ;
}
Entry->DisplayName.MaximumLength = Entry->DisplayName.Length + sizeof( WCHAR );
RtlCopyMemory(
Current,
DisplayBase->Buffer,
DisplayBase->Length );
Current += DisplayBase->Length ;
if ( Modifier )
{
RtlCopyMemory(
Current,
Modifier->Buffer,
Modifier->Length );
Current += Modifier->Length ;
}
*Current++ = '\0';
*Current++ = '\0';
return Entry ;
}
LONG
DCacheGetTrustedDomains(
PDOMAIN_CACHE_ARRAY * pArray
)
{
LONG NetStatus ;
PDOMAIN_CACHE_ARRAY Array ;
PDOMAIN_CACHE_ENTRY Entry ;
PDS_DOMAIN_TRUSTS Trusts ;
ULONG TrustCount ;
ULONG i ;
UNICODE_STRING Flat ;
UNICODE_STRING Dns ;
DOMAIN_ENTRY_TYPE Type ;
*pArray = NULL ;
NetStatus = DsEnumerateDomainTrusts(
NULL,
DS_DOMAIN_IN_FOREST |
DS_DOMAIN_DIRECT_OUTBOUND,
&Trusts,
&TrustCount );
if ( NetStatus != NERR_Success )
{
return NetStatus ;
}
Array = DCacheCreateArray( TrustCount + 5,
TRUE );
if ( !Array )
{
NetApiBufferFree( Trusts );
return ERROR_NOT_ENOUGH_MEMORY ;
}
for ( i = 0 ; i < TrustCount ; i++ )
{
if ( Trusts[ i ].NetbiosDomainName )
{
RtlInitUnicodeString( &Flat, Trusts[ i ].NetbiosDomainName );
}
else
{
ZeroMemory( &Flat, sizeof( Flat ) );
}
if ( Trusts[ i ].DnsDomainName )
{
RtlInitUnicodeString( &Dns, Trusts[ i ].DnsDomainName );
}
else
{
ZeroMemory( &Dns, sizeof( Dns ) );
}
switch ( Trusts[ i ].TrustType )
{
case TRUST_TYPE_DOWNLEVEL :
Type = DomainNt4 ;
break;
case TRUST_TYPE_UPLEVEL:
Type = DomainNt5 ;
break;
case TRUST_TYPE_MIT:
Type = DomainMitRealm ;
break;
default:
continue;
}
DebugLog(( DEB_TRACE_CACHE, "Processing domain (%d) %ws\n",
Type,
Trusts[ i ].NetbiosDomainName ));
Entry = DCacheCreateEntry(
Type,
( Flat.Buffer ? &Flat : NULL ),
( Dns.Buffer ? &Dns : NULL ),
NULL );
if ( Entry )
{
DCacheInsertArray(
Array,
Entry );
DCacheDereferenceEntry( Entry );
}
}
NetApiBufferFree( Trusts );
*pArray = Array ;
return 0 ;
}
BOOL
DCacheAddMitRealms(
PDOMAIN_CACHE_ARRAY Array
)
{
HKEY MitKey ;
DWORD Index ;
PWSTR Realms;
DWORD RealmSize;
int err ;
DWORD NumRealms;
DWORD MaxRealmLength ;
FILETIME KeyTime ;
PDOMAIN_CACHE_ENTRY Entry ;
PDOMAIN_CACHE_ENTRY TrustedDomain ;
UNICODE_STRING DnsName ;
err = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Control\\Lsa\\Kerberos\\Domains"),
0,
KEY_READ,
&MitKey );
if ( err == 0 )
{
err = RegQueryInfoKey( MitKey,
NULL,
NULL,
NULL,
&NumRealms,
&MaxRealmLength,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL );
MaxRealmLength++ ;
MaxRealmLength *= sizeof( WCHAR );
Realms = LocalAlloc( LMEM_FIXED, MaxRealmLength );
if ( Realms)
{
for ( Index = 0 ; Index < NumRealms ; Index++ )
{
RealmSize = MaxRealmLength ;
err = RegEnumKeyEx( MitKey,
Index,
Realms,
&RealmSize,
NULL,
NULL,
NULL,
&KeyTime );
if ( err == 0 )
{
DebugLog(( DEB_TRACE_CACHE, "Found realm %ws\n", Realms ));
RtlInitUnicodeString( &DnsName, Realms );
Entry = DCacheCreateEntry(
DomainMitUntrusted,
&DnsName,
&DnsName,
NULL );
if ( Entry )
{
Entry->Flags |= DCE_REACHABLE_MIT ;
if ( !DCacheInsertArray( Array, Entry ) )
{
//
// If the insert failed, then there's already an entry
// in the list for this domain. Locate it, and tag it
// so that it will be displayed
//
TrustedDomain = DCacheSearchArray( Array, &DnsName );
if ( TrustedDomain )
{
TrustedDomain->Flags |= DCE_REACHABLE_MIT ;
}
}
DCacheDereferenceEntry( Entry );
}
}
}
LocalFree( Realms );
}
RegCloseKey( MitKey );
}
return TRUE ;
}
BOOL
DCacheAddNetworkProviders(
PDOMAIN_CACHE_ARRAY Array
)
{
WCHAR szProviderName[128];
WCHAR szKeyPath[MAX_PATH];
PWSTR pszProviders;
PWSTR pszScan;
PWSTR pszStart;
WCHAR Save;
HKEY hKey;
DWORD dwType;
DWORD dwLen;
DWORD Class;
int err;
PDOMAIN_CACHE_ENTRY Entry ;
UNICODE_STRING String ;
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Control\\NetworkProvider\\Order"),
0,
KEY_READ,
&hKey );
if ( err )
{
return FALSE ;
}
err = RegQueryValueEx( hKey,
TEXT("ProviderOrder"),
NULL,
&dwType,
NULL,
&dwLen );
if ( (err) || (dwType != REG_SZ) )
{
RegCloseKey( hKey );
return FALSE ;
}
pszProviders = LocalAlloc( LMEM_FIXED, dwLen );
if ( !pszProviders )
{
RegCloseKey( hKey );
return FALSE ;
}
err = RegQueryValueEx( hKey,
TEXT("ProviderOrder"),
NULL,
&dwType,
(PUCHAR) pszProviders,
&dwLen );
RegCloseKey( hKey );
if ( err )
{
LocalFree( pszProviders );
return FALSE ;
}
//
// Initialize things.
//
pszStart = pszProviders;
szProviderName[0] = TEXT('<');
szProviderName[1] = TEXT(' ');
while ( *pszStart )
{
pszScan = pszStart;
while ( (*pszScan) && (*pszScan != TEXT(',') ) )
{
pszScan++;
}
Save = *pszScan;
*pszScan = TEXT('\0');
wsprintf( szKeyPath,
TEXT("System\\CurrentControlSet\\Services\\%s\\networkprovider"),
pszStart );
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
szKeyPath,
0,
KEY_READ,
&hKey );
if ( err == 0 )
{
dwLen = sizeof(DWORD) ;
err = RegQueryValueEx( hKey,
TEXT("Class"),
NULL,
&dwType,
(PUCHAR) &Class,
&dwLen );
if ( (err == 0) && (dwType == REG_DWORD) )
{
if ( Class & WN_CREDENTIAL_CLASS )
{
dwLen = 126 * sizeof(WCHAR);
err = RegQueryValueEx( hKey,
TEXT("Name"),
NULL,
&dwType,
(PUCHAR) &szProviderName[2],
&dwLen );
wcscpy( &szProviderName[ (dwLen / sizeof(WCHAR) ) + 2 ],
TEXT(" >") );
RtlInitUnicodeString( &String, szProviderName );
Entry = DCacheCreateEntry(
DomainNetworkProvider,
&String,
NULL,
NULL );
if ( Entry )
{
DCacheInsertArray( Array, Entry );
DCacheDereferenceEntry( Entry );
}
}
}
RegCloseKey( hKey );
}
*pszScan = Save;
if ( *pszScan )
{
pszStart = pszScan + 1;
}
else
{
pszStart = NULL;
break;
}
}
LocalFree( pszProviders );
return TRUE ;
}
BOOL
DCacheGetDomainsFromCache(
PDOMAIN_CACHE_ARRAY *pArray,
PLARGE_INTEGER RegistryTime
)
{
HKEY Key ;
int err ;
DWORD NumDomains ;
DWORD i ;
WCHAR FlatName[ DNLEN + 2 ];
WCHAR DnsDomain[ MAX_PATH ];
DWORD dwType ;
DWORD FlatNameSize ;
DWORD DnsDomainSize ;
UNICODE_STRING Flat ;
UNICODE_STRING Dns ;
PDOMAIN_CACHE_ENTRY Entry ;
PDOMAIN_CACHE_ARRAY Array ;
DWORD dwSize ;
PWSTR DomainBuffer ;
PWSTR DomainBufferEnd ;
PWSTR Scan ;
ULONG Disp ;
BOOL ReturnFalseAnyway = FALSE ;
if ( SafeBootMode == SAFEBOOT_MINIMAL )
{
RegistryTime->QuadPart = 0 ;
return FALSE ;
}
dwSize = 0 ;
err = RegQueryValueEx(
WinlogonKey,
szCacheValue,
NULL,
&dwType,
NULL,
&dwSize );
if ( ( err == ERROR_MORE_DATA ) ||
( err == ERROR_BUFFER_OVERFLOW ) ||
( err == 0 ) )
{
//
//
DomainBuffer = LocalAlloc( LMEM_FIXED, dwSize );
if ( DomainBuffer )
{
err = RegQueryValueEx(
WinlogonKey,
szCacheValue,
NULL,
&dwType,
(PUCHAR) DomainBuffer,
&dwSize );
if ( err == 0 )
{
DomainBufferEnd = (PWSTR)((PUCHAR) DomainBuffer + dwSize);
Scan = DomainBuffer ;
err = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
szCache,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&Key,
&Disp );
if ( err == 0 )
{
while ( Scan != DomainBufferEnd )
{
err = RegSetValueEx(
Key,
Scan,
0,
REG_SZ,
(PUCHAR) TEXT(""),
sizeof( WCHAR ) );
Scan += wcslen(Scan) ;
while ( (*Scan == L'\0' ) &&
(Scan != DomainBufferEnd ) )
{
Scan++ ;
}
}
RegCloseKey( Key );
}
}
LocalFree( DomainBuffer );
}
RegDeleteValue( WinlogonKey, szCacheValue );
ReturnFalseAnyway = TRUE ;
}
err = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
szCache,
0,
KEY_READ,
&Key );
if ( err )
{
return FALSE ;
}
err = RegQueryInfoKey( Key,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&NumDomains,
NULL,
NULL,
NULL,
NULL );
if ( *pArray )
{
Array = *pArray ;
}
else
{
Array = DCacheCreateArray( NumDomains + 5, TRUE );
}
if ( Array )
{
for ( i = 0 ; i < NumDomains ; i++ )
{
FlatNameSize = DNLEN + 2 ;
DnsDomainSize = MAX_PATH ;
err = RegEnumValue(
Key,
i,
FlatName,
&FlatNameSize,
NULL,
&dwType,
(PUCHAR) DnsDomain,
&DnsDomainSize );
if ( err == 0 )
{
RtlInitUnicodeString( &Flat, FlatName );
RtlInitUnicodeString( &Dns, DnsDomain );
Entry = DCacheCreateEntry(
( Dns.Length ? DomainNt5 : DomainNt4),
&Flat,
( Dns.Length ? &Dns : NULL ),
NULL );
if ( Entry )
{
DCacheInsertArray( Array, Entry );
DCacheDereferenceEntry( Entry );
}
}
}
}
RegCloseKey( Key );
if ( RegistryTime )
{
dwSize = sizeof( LARGE_INTEGER ) ;
if ( RegQueryValueEx( WinlogonKey,
szCacheUpdate,
0,
&dwType,
(PUCHAR) RegistryTime,
&dwSize ) ||
(dwType != REG_BINARY ) ||
(dwSize != sizeof( LARGE_INTEGER ) ) )
{
RegistryTime->QuadPart = 0 ;
}
}
*pArray = Array ;
if ( ReturnFalseAnyway )
{
return FALSE ;
}
else
{
return TRUE ;
}
}
PDOMAIN_CACHE_ENTRY
DCacheEntryFromRegistry(
PUNICODE_STRING FlatName
)
{
PDOMAIN_CACHE_ENTRY Entry = NULL ;
HKEY Key ;
int err ;
DWORD dwType ;
WCHAR DnsName[ MAX_PATH ];
DWORD dwSize ;
UNICODE_STRING Dns ;
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
szCache,
0,
KEY_READ,
&Key );
if ( err == 0 )
{
dwSize = MAX_PATH ;
DnsName[ 0 ] = L'\0';
err = RegQueryValueEx(
Key,
FlatName->Buffer,
NULL,
&dwType,
(PUCHAR) DnsName,
&dwSize );
if ( err == 0 )
{
if ( dwType == REG_SZ )
{
RtlInitUnicodeString( &Dns, DnsName );
Entry = DCacheCreateEntry(
( Dns.Length ? DomainNt5 : DomainNt4 ),
FlatName,
( Dns.Length ? &Dns : NULL ),
NULL );
}
}
RegCloseKey( Key );
}
return Entry ;
}
VOID
DCacheWriteDomainsToCache(
PDOMAIN_CACHE_ARRAY Array
)
{
HKEY Key ;
ULONG i ;
ULONG Disp ;
int err ;
PDOMAIN_CACHE_ENTRY Entry ;
LARGE_INTEGER Now ;
//
// Delete what's there. Ignore the error, since we are
// just going to rewrite all the values, anyway.
//
err = RegDeleteKey( HKEY_LOCAL_MACHINE,
szCache );
err = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
szCache,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&Key,
&Disp );
if ( err )
{
return ;
}
for ( i = 0 ; i < Array->Count ; i++ )
{
Entry = Array->List[ i ];
if ( ( Entry->Type == DomainNt5 ) )
{
if ( ( Entry->FlatName.Buffer == NULL ) ||
( Entry->DnsName.Buffer == NULL ) )
{
DebugLog(( DEB_ERROR, "Corrupt uplevel domain cache entry at %p\n", Entry ));
continue;
}
RegSetValueEx(
Key,
Entry->FlatName.Buffer,
0,
REG_SZ,
(PUCHAR) Entry->DnsName.Buffer,
Entry->DnsName.Length + sizeof(WCHAR) );
}
else if ( Entry->Type == DomainNt4 )
{
if ( Entry->FlatName.Buffer == NULL )
{
DebugLog(( DEB_ERROR, "Corrupt downlevel domain cache entry at %p\n", Entry ));
}
RegSetValueEx(
Key,
Entry->FlatName.Buffer,
0,
REG_SZ,
(PUCHAR) TEXT(""),
sizeof(WCHAR) );
}
else
{
//
// Other types don't get to live in the cache
//
NOTHING ;
}
}
RegCloseKey( Key );
GetSystemTimeAsFileTime( (LPFILETIME) &Now );
RegSetValueEx(
WinlogonKey,
szCacheUpdate,
0,
REG_BINARY,
(PUCHAR) &Now,
sizeof( LARGE_INTEGER ) );
}
BOOL
DCacheInitialize(
VOID
)
{
DOMAIN_ENTRY_TYPE Types ;
WCHAR StringBuffer[ MAX_PATH ];
LONG Size ;
int err ;
DWORD dwSize ;
DWORD dwType ;
for (Types = 0 ; Types < DomainTypeMax ; Types++ )
{
if ( CacheDomainModifiers[ Types ].StringId )
{
Size = LoadString( hDllInstance,
CacheDomainModifiers[ Types ].StringId,
StringBuffer,
MAX_PATH );
if ( Size )
{
RtlCreateUnicodeString( &CacheDomainModifiers[ Types ].String,
StringBuffer );
}
}
}
dwSize = sizeof( CacheShowDnsNames );
err = RegQueryValueEx(
WinlogonKey,
DCACHE_SHOW_DNS_NAMES,
NULL,
&dwType,
(PUCHAR) &CacheShowDnsNames,
&dwSize );
dwSize = sizeof( CacheAppendDomainInfo );
err = RegQueryValueEx(
WinlogonKey,
DCACHE_SHOW_DOMAIN_TAGS,
NULL,
&dwType,
(PUCHAR) &CacheAppendDomainInfo,
&dwSize );
//
// Convert and delete old cache:
//
return TRUE ;
}
BOOL
DCachepInitializeCache(
PDOMAIN_CACHE pCache
)
{
NTSTATUS Status ;
ZeroMemory( pCache, sizeof(DOMAIN_CACHE) );
Status = RtlInitializeCriticalSectionAndSpinCount(
& pCache->CriticalSection,
0x80000000 );
return NT_SUCCESS( Status );
}
PDOMAIN_CACHE
DCacheCreate(
VOID
)
{
PDOMAIN_CACHE Cache ;
Cache = (PDOMAIN_CACHE) LocalAlloc( LMEM_FIXED, sizeof( DOMAIN_CACHE ) );
if ( !Cache )
{
return NULL ;
}
if ( !DCachepInitializeCache( Cache ) )
{
LocalFree( Cache );
return NULL ;
}
Cache->State = DomainCacheEmpty ;
if ( !g_Console )
{
Cache->Flags |= DCACHE_READ_ONLY ;
}
if ( SafeBootMode == SAFEBOOT_MINIMAL )
{
Cache->Flags |= DCACHE_MIT_MODE ;
}
return Cache ;
}
BOOL
DCacheGetMinimalArray(
PDOMAIN_CACHE_ARRAY Array,
PWSTR DefaultDomain OPTIONAL,
PBOOL DomainMember OPTIONAL,
PBOOL NewDomain OPTIONAL
)
{
BOOL SidPresent = FALSE ;
UNICODE_STRING String = { 0 } ;
UNICODE_STRING DnsDomain = { 0 } ;
PDOMAIN_CACHE_ENTRY Entry = NULL ;
PDOMAIN_CACHE_ENTRY ComputerEntry = NULL ;
PDOMAIN_CACHE_ENTRY OldDefault = NULL ;
NT_PRODUCT_TYPE ProductType = NtProductWinNt;
WCHAR ComputerName[ CNLEN + 1 ];
ULONG Size ;
ULONG Type ;
WCHAR LastPrimary[ DNLEN + 1 ];
UNICODE_STRING LastPrimary_U ;
//
// First, find out what we are
//
RtlGetNtProductType( &ProductType );
if ( Array == NULL )
{
Array = DCacheCreateArray( 5, TRUE );
}
if ( Array == NULL )
{
return FALSE ;
}
if ( SafeBootMode != SAFEBOOT_MINIMAL )
{
if ( GetPrimaryDomainEx( &String, &DnsDomain, NULL, &SidPresent ) )
{
//
// Ok, we are configured to be part of a domain.
//
if ( SidPresent )
{
//
// Ok, this is an NT domain.
//
Entry = DCacheCreateEntry(
( DnsDomain.Buffer ? DomainNt5 : DomainNt4 ),
&String,
&DnsDomain,
NULL );
if ( Entry )
{
if ( ProductType == NtProductLanManNt )
{
//
// We're a DC. Until we know otherwise, tag this as the default
//
Entry->Flags |= DCE_DEFAULT_ENTRY ;
}
DCacheInsertArray( Array, Entry );
DCacheDereferenceEntry( Entry );
Entry = NULL ;
}
//
// Check to see if we've changed domains:
//
if ( NewDomain )
{
Size = sizeof( LastPrimary );
if ( RegQueryValueEx(
WinlogonKey,
szCachePrimary,
0,
&Type,
(PUCHAR) LastPrimary,
&Size ) == 0 )
{
RtlInitUnicodeString( &LastPrimary_U, LastPrimary );
*NewDomain = !RtlEqualUnicodeString( &LastPrimary_U,
&String,
TRUE );
}
else
{
//
// If the value can't be read for any reason, assume that it's
// missing and we're in a different domain than last time.
//
*NewDomain = TRUE ;
}
}
RegSetValueEx(
WinlogonKey,
szCachePrimary,
0,
REG_SZ,
(PUCHAR) String.Buffer,
String.Length + sizeof(WCHAR));
}
else
{
//
// Part of an MIT realm, skip for now. It will get added
// below when all the MIT realms are added.
//
NOTHING ;
}
if ( String.Buffer )
{
LocalFree( String.Buffer );
}
if ( DnsDomain.Buffer )
{
LocalFree( DnsDomain.Buffer );
}
}
}
if ( ( ProductType != NtProductLanManNt ) ||
( SafeBootMode == SAFEBOOT_MINIMAL ) )
{
//
// Do the machine name:
//
Size = CNLEN + 1;
GetComputerName( ComputerName, &Size );
RtlInitUnicodeString( &String, ComputerName );
ComputerEntry = DCacheCreateEntry(
DomainMachine,
&String,
NULL,
NULL );
if ( ComputerEntry )
{
DCacheInsertArray( Array, ComputerEntry );
DCacheDereferenceEntry( ComputerEntry );
}
}
DCacheAddMitRealms( Array );
if ( DefaultDomain && (*DefaultDomain) )
{
RtlInitUnicodeString( &String, DefaultDomain );
OldDefault = DCacheFindDefaultEntry( Array );
if ( Entry )
{
Entry->Flags &= ~(DCE_DEFAULT_ENTRY);
}
Entry = DCacheSearchArray( Array, &String );
if ( !Entry )
{
Entry = DCacheEntryFromRegistry( &String );
if ( Entry )
{
DCacheInsertArray( Array, Entry );
}
}
else
{
DCacheReferenceEntry( Entry );
}
if ( Entry )
{
Entry->Flags |= DCE_DEFAULT_ENTRY ;
DCacheDereferenceEntry( Entry );
if ( OldDefault )
{
OldDefault->Flags &= ~(DCE_DEFAULT_ENTRY);
}
}
}
if ( DomainMember )
{
*DomainMember = SidPresent ;
}
return TRUE ;
}
BOOL
DCacheUpdateMinimal(
PDOMAIN_CACHE Cache,
PWSTR DefaultDomain OPTIONAL,
BOOL CompleteAsync
)
{
PDOMAIN_CACHE_ARRAY Array = NULL ;
LARGE_INTEGER RegistryTime = { 0 };
BOOL DomainMember = FALSE ;
BOOL NewDomain = FALSE ;
BOOL RetryDomain = FALSE ;
BOOL NoCache = FALSE ;
BOOL StartThread = FALSE ;
HANDLE hThread ;
DWORD tid ;
WCHAR ComputerName[ 20 ];
ULONG Size ;
if ( !DCacheGetDomainsFromCache( &Array, &RegistryTime ) ||
( Array == NULL ) )
{
NoCache = TRUE ;
}
//
// In rare events, we will leave a domain, and the cache
// will still be in the registry. This is caught later,
// and deleted, and this is the retry point.
//
ReloadWithoutCache:
if ( !Array )
{
Array = DCacheCreateArray( 5, TRUE );
if ( !Array )
{
return FALSE ;
}
}
if ( !DCacheGetMinimalArray( Array,
DefaultDomain,
&DomainMember,
&NewDomain ) )
{
DCacheFreeArray( Array );
return FALSE ;
}
//
// If we are no longer in the same domain, either in a workgroup, or
// in a different domain, toss the cache. If we just retried this
// don't keep doing it...
//
if ( ( RetryDomain == FALSE ) &&
( ( ( NoCache == FALSE ) &&
( DomainMember == FALSE ) ) ||
( NewDomain == TRUE ) ) )
{
//
// Cleanup. The cache is still present, but we are no longer part of a domain
//
DCacheFreeArray( Array );
RegDeleteKey( HKEY_LOCAL_MACHINE, szCache );
RegDeleteValue( WinlogonKey, szCachePrimary );
if ( DefaultDomain )
{
Size = 20 ;
if ( GetComputerName( ComputerName, &Size ) )
{
if ( _wcsicmp( DefaultDomain, ComputerName ) )
{
DefaultDomain = NULL ;
RegSetValueEx(
WinlogonKey,
DEFAULT_DOMAIN_NAME_KEY,
0,
REG_SZ,
(PUCHAR) ComputerName,
(Size + 1) * sizeof( WCHAR ) );
}
}
}
NoCache = TRUE ;
Array = NULL ;
RetryDomain = TRUE ;
goto ReloadWithoutCache;
}
LockDomainCache( Cache );
if ( Cache->Array )
{
DCacheFreeArray( Cache->Array );
}
Cache->Array = Array ;
Cache->RegistryUpdateTime = RegistryTime ;
if ( NoCache )
{
Cache->Flags |= DCACHE_NO_CACHE ;
}
GetSystemTimeAsFileTime( (LPFILETIME) &Cache->CacheUpdateTime );
if ( DomainMember )
{
if ( !NoCache )
{
if ( Cache->CacheUpdateTime.QuadPart - Cache->RegistryUpdateTime.QuadPart < TWO_WEEKS )
{
Cache->State = DomainCacheRegistryCache ;
}
else
{
Cache->State = DomainCacheDefaultOnly ;
}
}
else
{
Cache->State = DomainCacheDefaultOnly ;
}
Cache->Flags |= DCACHE_MEMBER ;
}
else
{
Cache->State = DomainCacheReady ;
}
if ( DCacheFindDefaultEntry( Array ) == NULL )
{
Cache->Flags |= DCACHE_DEF_UNKNOWN ;
}
if ( ( Cache->State != DomainCacheReady ) &&
( CompleteAsync ) )
{
StartThread = TRUE ;
if ( DefaultDomain )
{
Cache->DefaultDomain = DupString( DefaultDomain );
}
else
{
Cache->DefaultDomain = NULL ;
}
}
UnlockDomainCache( Cache );
if ( StartThread )
{
hThread = CreateThread( NULL,
0,
DCacheUpdateThread,
Cache,
0,
&tid );
if ( hThread )
{
CloseHandle( hThread );
}
else
{
LockDomainCache( Cache );
Cache->State = DomainCacheReady ;
UnlockDomainCache( Cache );
}
}
return TRUE ;
}
BOOL
DCacheUpdateFull(
PDOMAIN_CACHE Cache,
PWSTR Default OPTIONAL
)
{
PDOMAIN_CACHE_ARRAY Array = NULL ;
ULONG NetStatus = 0 ;
ULONG RetryCount = 3 ;
BOOL DomainMember = FALSE ;
NT_PRODUCT_TYPE ProductType = NtProductWinNt;
LARGE_INTEGER RegistryTime = { 0 };
if ( ( Cache->Flags & DCACHE_MEMBER ) != 0 )
{
DomainMember = TRUE ;
RtlGetNtProductType( &ProductType );
if ( ProductType == NtProductLanManNt )
{
RetryCount = 3600 ;
}
//
// now, call netlogon, and see if it has the list.
//
NetStatus = DCacheGetTrustedDomains( &Array );
if ( NetStatus != 0 )
{
while ( RetryCount-- )
{
Sleep( 3000 );
NetStatus = DCacheGetTrustedDomains( &Array );
if ( NetStatus == 0 )
{
break;
}
}
}
if ( NetStatus != 0 )
{
//
// Try to read from the cache
//
DCacheGetDomainsFromCache( &Array, &RegistryTime );
}
}
if ( Array )
{
DCacheGetMinimalArray( Array, Default, &DomainMember, NULL );
}
if ( Array )
{
LockDomainCache( Cache );
if ( Cache->Array )
{
DCacheFreeArray( Cache->Array );
}
Cache->Array = Array ;
if ( DomainMember )
{
Cache->Flags |= DCACHE_MEMBER ;
}
else
{
Cache->Flags &= ~( DCACHE_MEMBER ) ;
}
if ( NetStatus == 0 )
{
Cache->State = DomainCacheReady ;
if ( ( Cache->Flags & DCACHE_READ_ONLY ) == 0 )
{
DCacheWriteDomainsToCache( Array );
}
GetSystemTimeAsFileTime( (LPFILETIME) &Cache->RegistryUpdateTime );
Cache->Flags &= ~(DCACHE_NO_CACHE);
}
else if ( (Cache->Flags & DCACHE_NO_CACHE) == 0 )
{
Cache->State = DomainCacheRegistryCache ;
Cache->RegistryUpdateTime = RegistryTime ;
}
else
{
Cache->State = DomainCacheDefaultOnly ;
Cache->RegistryUpdateTime.QuadPart = 0 ;
}
if ( Cache->DefaultDomain )
{
DCacheSetDefaultEntry( Cache,
Cache->DefaultDomain,
NULL );
Free( Cache->DefaultDomain );
Cache->DefaultDomain = NULL ;
}
UnlockDomainCache( Cache );
}
return ( Array != NULL ) ;
}
DWORD
DCacheUpdateThread(
PDOMAIN_CACHE Cache
)
{
HWND Notify ;
UINT Message ;
LockDomainCache( Cache );
if ( ( Cache->Flags & DCACHE_ASYNC_UPDATE ) != 0 )
{
//
// Another thread is already doing this.
//
UnlockDomainCache( Cache );
return 0 ;
}
Cache->Flags |= DCACHE_ASYNC_UPDATE ;
UnlockDomainCache( Cache );
DCacheUpdateFull( Cache, NULL );
LockDomainCache( Cache );
Notify = Cache->UpdateNotifyWindow ;
Message = Cache->Message ;
Cache->UpdateNotifyWindow = NULL ;
Cache->Message = 0 ;
Cache->Flags &= ~( DCACHE_ASYNC_UPDATE );
UnlockDomainCache( Cache );
if ( Notify )
{
DebugLog(( DEB_TRACE_CACHE, "Notifying window %x of cache complete\n" ));
PostMessage( Notify, Message, 0, 0 );
}
return 0;
}
BOOL
DCachePopulateListBoxFromArray(
PDOMAIN_CACHE_ARRAY Array,
HWND ComboBox,
LPWSTR LastKey OPTIONAL
)
{
ULONG i ;
ULONG_PTR Index ;
PDOMAIN_CACHE_ENTRY Default = NULL ;
LRESULT Result ;
//
// Reset the combo box
//
DebugLog((DEB_TRACE_CACHE, "Flushing listbox\n" ));
SendMessage( ComboBox, CB_RESETCONTENT, 0, 0);
for ( i = 0 ; i < Array->Count ; i++ )
{
DebugLog(( DEB_TRACE_CACHE, "Adding domain %ws (%d) to listbox\n",
Array->List[ i ]->DisplayName.Buffer,
Array->List[ i ]->Type ));
if ( Array->List[ i ]->Type == DomainMitRealm )
{
if ( (Array->List[ i ]->Flags & DCE_REACHABLE_MIT ) == 0 )
{
DebugLog(( DEB_TRACE_CACHE, "MIT Realm %ws is not reachable, skipping\n",
Array->List[ i ]->FlatName.Buffer ));
continue;
}
}
Index = SendMessage( ComboBox,
CB_ADDSTRING,
0,
(LPARAM) Array->List[ i ]->DisplayName.Buffer );
if ( Index != CB_ERR )
{
SendMessage( ComboBox,
CB_SETITEMDATA,
(WPARAM) Index,
(LPARAM) Array->List[ i ] );
}
if ( ( Array->List[ i ]->Type == DomainMachine ) &&
( Default == NULL ) )
{
Default = Array->List[ i ] ;
}
if ( Array->List[ i ]->Flags & DCE_DEFAULT_ENTRY )
{
Default = Array->List[ i ];
}
}
//
// Select the default entry:
//
if ( LastKey && (*LastKey) )
{
Result = SendMessage( ComboBox,
CB_SELECTSTRING,
(WPARAM) -1,
(LPARAM) LastKey );
#if DBG
if ( Result != CB_ERR )
{
DebugLog(( DEB_TRACE_CACHE, "Selected first entry starting with %ws\n", LastKey ));
}
else
{
DebugLog(( DEB_TRACE_CACHE, "No entry found starting with %ws. Trying default\n", LastKey ));
}
#endif
}
else
{
Result = CB_ERR ;
}
if ( ( Result == CB_ERR ) &&
( Default != NULL ) )
{
SendMessage( ComboBox,
CB_SELECTSTRING,
(WPARAM) -1,
(LPARAM) Default->DisplayName.Buffer );
DebugLog(( DEB_TRACE_CACHE, "Selecting '%ws' as the default entry\n",
Default->DisplayName.Buffer ));
}
return TRUE ;
}
BOOL
DCacheSetNotifyWindowIfNotReady(
PDOMAIN_CACHE Cache,
HWND Window,
UINT Message
)
{
BOOL IsReady = FALSE ;
HANDLE hThread ;
DWORD tid ;
LockDomainCache( Cache );
IsReady = ( Cache->State == DomainCacheReady );
if ( !IsReady )
{
Cache->UpdateNotifyWindow = Window ;
Cache->Message = Message ;
}
else
{
if ( ( Cache->Flags & DCACHE_ASYNC_UPDATE ) == 0 )
{
hThread = CreateThread( NULL,
0,
DCacheUpdateThread,
Cache,
0,
&tid );
}
}
UnlockDomainCache( Cache );
return IsReady ;
}
PDOMAIN_CACHE_ARRAY
DCacheCopyCacheArray(
PDOMAIN_CACHE Cache
)
{
PDOMAIN_CACHE_ARRAY Array = NULL ;
LockDomainCache( Cache );
if ( Cache->Array )
{
Array = DCacheCopyArray( Cache->Array );
}
UnlockDomainCache( Cache );
return Array ;
}
BOOL
DCacheValidateCache(
PDOMAIN_CACHE Cache
)
{
LARGE_INTEGER Now ;
LARGE_INTEGER Diff ;
LockDomainCache( Cache );
GetSystemTimeAsFileTime( (LPFILETIME) &Now );
Diff.QuadPart = Now.QuadPart - Cache->CacheUpdateTime.QuadPart ;
UnlockDomainCache( Cache );
return (Diff.QuadPart < TWO_MINUTES );
}
DOMAIN_CACHE_STATE
DCacheGetCacheState(
PDOMAIN_CACHE Cache
)
{
DOMAIN_CACHE_STATE State ;
LockDomainCache( Cache );
State = Cache->State ;
UnlockDomainCache( Cache );
return State ;
}
BOOL
DCacheSetDefaultEntry(
PDOMAIN_CACHE Cache,
PWSTR FlatName OPTIONAL,
PWSTR DnsName OPTIONAL
)
{
UNICODE_STRING String ;
PDOMAIN_CACHE_ENTRY Entry ;
BOOL Result ;
if ( ( FlatName == NULL ) &&
( DnsName == NULL ) )
{
return FALSE ;
}
LockDomainCache( Cache );
Entry = DCacheFindDefaultEntry( Cache->Array );
if ( Entry )
{
Entry->Flags &= ~(DCE_DEFAULT_ENTRY) ;
}
if ( FlatName )
{
RtlInitUnicodeString( &String, FlatName );
Entry = DCacheSearchArray( Cache->Array,
&String );
}
else
{
RtlInitUnicodeString( &String, DnsName );
Entry = DCacheSearchArrayByDns( Cache->Array,
&String );
}
if ( Entry )
{
Entry->Flags |= DCE_DEFAULT_ENTRY ;
Result = TRUE ;
DebugLog(( DEB_TRACE_CACHE, "Setting '%ws' to be the default\n",
Entry->DisplayName.Buffer ));
}
else
{
Result = FALSE ;
}
if ( Result )
{
Cache->Flags &= ~(DCACHE_DEF_UNKNOWN) ;
}
else
{
Cache->Flags |= DCACHE_DEF_UNKNOWN ;
}
UnlockDomainCache( Cache );
return Result ;
}
PDOMAIN_CACHE_ENTRY
DCacheLocateEntry(
PDOMAIN_CACHE Cache,
PWSTR Domain
)
{
PDOMAIN_CACHE_ENTRY Entry = NULL ;
UNICODE_STRING String ;
LockDomainCache( Cache );
if ( Domain )
{
RtlInitUnicodeString( &String, Domain );
Entry = DCacheSearchArray( Cache->Array,
&String );
if ( Entry )
{
DCacheReferenceEntry( Entry );
}
}
UnlockDomainCache( Cache );
return Entry ;
}
ULONG
DCacheGetFlags(
PDOMAIN_CACHE Cache
)
{
ULONG Flags ;
LockDomainCache( Cache );
Flags = Cache->Flags ;
UnlockDomainCache( Cache );
return Flags ;
}