📄 nlpcache.c
字号:
//
// A handle to the LsaPolicy object will be open (NlpLsaHandle).
//
// A handle to the registry key in which all cache entries
// are held will be open (NlpCacheHandle).
//
// A global structure defining how many cache entries there are
// will be initialized (NlpCacheControl).
//
// The Cache Table Entry table (CTE table) will be initialized
// (NlpCteTable).
//
// The active and inactive CTE lists will be built
// (NlpActiveCtes and NlpInactiveCtes).
//
// A global cache encryption key will be initialized.
//
ENTER_CACHE();
//
// Check again if the cache is initialized now that the crit sect is locked.
//
if (NlpInitializationNotYetPerformed) {
//
// Open the local system's policy object
//
InitializeObjectAttributes(&ObjectAttributes,
NULL, // name
0,
0,
NULL
);
NtStatus = I_LsaIOpenPolicyTrusted( &NlpLsaHandle );
if (NT_SUCCESS(NtStatus)) {
//
// Successfully, or unsucessfully,
// The definition of "initialized" is we could call LSA's RPC
// routines.
//
NlpInitializationNotYetPerformed = FALSE;
//
// Open the registry key containing cache entries.
// This will remain open.
//
NtStatus = NlpOpenCache();
if (NT_SUCCESS(NtStatus)) {
//
// Get information on the current cache structure
// (number of entries, et cetera). This information is
// placed in a global variable for use throughout this
// module.
//
NtStatus = NlpGetCacheControlInfo();
//
// Initialize the per-machine cache encryption key.
//
if(NT_SUCCESS( NtStatus) ) {
NtStatus = NlpCacheKeyInitialize();
}
//
// Now build the CTE table
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = NlpBuildCteTable();
}
//
// If we were successful, then see if we need to change
// the cache due to new user-specified cache size.
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = NlpChangeCacheSizeIfNecessary();
}
if (!NT_SUCCESS(NtStatus)) {
NlpCloseCache();
}
}
if (!NT_SUCCESS(NtStatus)) {
I_LsarClose( &NlpLsaHandle );
}
}
//
// If we had an error, then set our entry count to zero
// to prevent using any cache information.
//
if (!NT_SUCCESS(NtStatus)) {
NlpCacheControl.Entries = 0;
}
} else {
NtStatus = STATUS_SUCCESS;
}
LEAVE_CACHE();
return(NtStatus);
}
NTSTATUS
NlpCacheKeyInitialize(
VOID
)
/*++
Routine Description:
Initializes the Global variable NlpCacheEncryptionKey with a per-machine
cache encryption key. If the per-machine key does not exist as an LSA
secret, it is created.
--*/
{
LSAPR_HANDLE SecretHandle;
UNICODE_STRING ValueName;
BOOLEAN SecretCreationNeeded = FALSE;
NTSTATUS NtStatus;
RtlInitUnicodeString( &ValueName, NLP_CACHE_ENCRYPTION_KEY_NAME );
NtStatus = I_LsarOpenSecret(NlpLsaHandle,
(PLSAPR_UNICODE_STRING) &ValueName,
SECRET_QUERY_VALUE | SECRET_SET_VALUE,
&SecretHandle
);
if (!NT_SUCCESS(NtStatus)) {
//
// create new key, if not present.
//
if (NtStatus != STATUS_OBJECT_NAME_NOT_FOUND) {
return (NtStatus);
}
NtStatus = I_LsarCreateSecret(NlpLsaHandle,
(PLSAPR_UNICODE_STRING) &ValueName,
SECRET_SET_VALUE,
&SecretHandle
);
if (!NT_SUCCESS(NtStatus)) {
return (NtStatus);
}
SecretCreationNeeded = TRUE;
} else {
//
// query current value...
//
LARGE_INTEGER
CurrentTime;
PLSAPR_CR_CIPHER_VALUE CurrentSecret = NULL;
NtStatus = I_LsarQuerySecret(SecretHandle,
&CurrentSecret,
&CurrentTime,
NULL,
NULL
);
if(NT_SUCCESS( NtStatus ) ) {
if( CurrentSecret == NULL ) {
//
// non existing data, create it.
//
SecretCreationNeeded = TRUE;
} else {
//
// size of data is wrong, bail now and leave things as-is.
//
if( CurrentSecret->Length != sizeof( NlpCacheEncryptionKey ) ) {
NtStatus = STATUS_SECRET_TOO_LONG;
} else {
//
// capture existing data into global.
//
CopyMemory( NlpCacheEncryptionKey, CurrentSecret->Buffer, CurrentSecret->Length );
}
MIDL_user_free(CurrentSecret);
}
}
}
if( SecretCreationNeeded ) {
LSAPR_CR_CIPHER_VALUE SecretValue;
SspGenerateRandomBits( NlpCacheEncryptionKey, sizeof(NlpCacheEncryptionKey) );
//
// write out secret...
//
SecretValue.Length = sizeof(NlpCacheEncryptionKey);
SecretValue.MaximumLength = SecretValue.Length;
SecretValue.Buffer = (PBYTE)NlpCacheEncryptionKey;
NtStatus = I_LsarSetSecret(SecretHandle,
&SecretValue,
NULL
);
}
I_LsarClose( &SecretHandle );
return (NtStatus);
}
NTSTATUS
NlpEncryptCacheEntry(
IN PLOGON_CACHE_ENTRY CacheEntry,
IN ULONG EntrySize
)
/*++
Routine Description:
Encrypts the sensitive portions of the input CacheEntry.
--*/
{
HMACMD5_CTX hmacCtx;
RC4_KEYSTRUCT rc4key;
CHAR DerivedKey[ MD5DIGESTLEN ];
PBYTE pbData;
ULONG cbData;
if( CacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
return STATUS_SUCCESS;
}
//
// derive encryption key from global machine LSA secret, and random
// cache entry key.
//
HMACMD5Init(&hmacCtx, NlpCacheEncryptionKey, sizeof(NlpCacheEncryptionKey));
HMACMD5Update(&hmacCtx, CacheEntry->RandomKey, sizeof(CacheEntry->RandomKey));
HMACMD5Final(&hmacCtx, DerivedKey);
//
// begin encrypting at the cachepasswords field.
//
pbData = (PBYTE)&(CacheEntry->CachePasswords);
//
// data length is EntrySize - header up to CachePasswords.
//
cbData = EntrySize - (ULONG)( pbData - (PBYTE)CacheEntry );
//
// MAC the data for integrity checking.
//
HMACMD5Init(&hmacCtx, DerivedKey, sizeof(DerivedKey));
HMACMD5Update(&hmacCtx, pbData, cbData);
HMACMD5Final(&hmacCtx, CacheEntry->MAC);
//
// now encrypt it...
//
rc4_key( &rc4key, sizeof(DerivedKey), DerivedKey );
rc4( &rc4key, cbData, pbData );
ZeroMemory( DerivedKey, sizeof(DerivedKey) );
return STATUS_SUCCESS;
}
NTSTATUS
NlpDecryptCacheEntry(
IN PLOGON_CACHE_ENTRY CacheEntry,
IN ULONG EntrySize
)
/*++
Routine Description:
Decrypts the sensitive portions of the input CacheEntry, and verified
integrity of decrypted data.
--*/
{
HMACMD5_CTX hmacCtx;
RC4_KEYSTRUCT rc4key;
CHAR DerivedKey[ MD5DIGESTLEN ];
CHAR MAC[ MD5DIGESTLEN ];
PBYTE pbData;
ULONG cbData;
if( CacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
return STATUS_SUCCESS;
}
//
// derive encryption key from global machine LSA secret, and random
// cache entry key.
//
HMACMD5Init(&hmacCtx, NlpCacheEncryptionKey, sizeof(NlpCacheEncryptionKey));
HMACMD5Update(&hmacCtx, CacheEntry->RandomKey, sizeof(CacheEntry->RandomKey));
HMACMD5Final(&hmacCtx, DerivedKey);
//
// begin decrypting at the cachepasswords field.
//
pbData = (PBYTE)&(CacheEntry->CachePasswords);
//
// data length is EntrySize - header up to CachePasswords.
//
cbData = EntrySize - (ULONG)( pbData - (PBYTE)CacheEntry );
//
// now decrypt it...
//
rc4_key( &rc4key, sizeof(DerivedKey), DerivedKey );
rc4( &rc4key, cbData, pbData );
//
// compute MAC on decrypted data for integrity checking.
//
HMACMD5Init(&hmacCtx, DerivedKey, sizeof(DerivedKey));
HMACMD5Update(&hmacCtx, pbData, cbData);
HMACMD5Final(&hmacCtx, MAC);
ZeroMemory( DerivedKey, sizeof(DerivedKey) );
//
// verify MAC.
//
if( memcmp( MAC, CacheEntry->MAC, sizeof(MAC) ) != 0 ) {
return STATUS_LOGON_FAILURE;
}
return STATUS_SUCCESS;
}
NTSTATUS
NlpBuildCacheEntry(
IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo,
OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
OUT PULONG pEntryLength
)
/*++
Routine Description:
Builds a LOGON_CACHE_ENTRY from a NETLOGON_VALIDATION_SAM_INFO2 structure.
We only cache those fields that we cannot easily re-invent
Arguments:
LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure containing
user's name and logon domain name
AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO2 from successful
logon
ppCacheEntry - pointer to place to return pointer to allocated
LOGON_CACHE_ENTRY
pEntryLength - size of the buffer returned in *ppCacheEntry
Return Value:
NTSTATUS
Success = STATUS_SUCCESS
*ppCacheEntry contains pointer to allocated LOGON_CACHE_ENTRY
structure
Failure = STATUS_NO_MEMORY
*ppCacheEntry undefined
--*/
{
PLOGON_CACHE_ENTRY pEntry;
ULONG length;
PCHAR dataptr;
UNICODE_STRING SamAccountName;
UNICODE_STRING NetbiosDomainName;
UNICODE_STRING DnsDomainName;
UNICODE_STRING Upn;
NTSTATUS NtStatus;
//
// Grab the various forms of the account name
//
NlpGetAccountNames( &LogonInfo->Identity,
AccountInfo,
&SamAccountName,
&NetbiosDomainName,
&DnsDomainName,
&Upn );
//
// assumes GROUP_MEMBERSHIP is integral multiple of DWORDs
//
length = ROUND_UP_COUNT(sizeof(LOGON_CACHE_ENTRY), sizeof(ULONG))
+ ROUND_UP_COUNT(NetbiosDomainName.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(SamAccountName.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(DnsDomainName.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(Upn.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->EffectiveName.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->FullName.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->LogonScript.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->ProfilePath.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->HomeDirectory.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->HomeDirectoryDrive.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->LogonDomainName.Length, sizeof(ULONG))
+ ROUND_UP_COUNT(AccountInfo->GroupCount * sizeof(GROUP_MEMBERSHIP), sizeof(ULONG))
+ ROUND_UP_COUNT(RtlLengthSid(AccountInfo->LogonDomainId), sizeof(ULONG));
if (AccountInfo->UserFlags & LOGON_EXTRA_SIDS) {
if (AccountInfo->SidCount) {
ULONG i;
length += ROUND_UP_COUNT(AccountInfo->SidCount * sizeof(ULONG), sizeof(ULONG));
for (i = 0; i < AccountInfo->SidCount ; i++ ) {
length += ROUND_UP_COUNT(RtlLengthSid(AccountInfo->ExtraSids[i].Sid), sizeof(ULONG));
}
}
}
pEntry = AllocateCacheEntry(length);
if (pEntry == NULL) {
return STATUS_NO_MEMORY;
}
RtlZeroMemory( pEntry, length );
pEntry->Revision = NLP_CACHE_REVISION;
NtQuerySystemTime( &pEntry->Time );
pEntry->Valid = TRUE;
pEntry->LogonPackage = LogonInfo->Identity.ParameterControl;
dataptr = (PCHAR)(pEntry + 1);
*pEntryLength = length;
ASSERT(!((ULONG_PTR)dataptr & (sizeof(ULONG) - 1)));
//
// each of these (unicode) strings and other structures are copied to the
// end of the fixed LOGON_CACHE_ENTRY structure, each aligned on DWORD
// boundaries
//
length = pEntry->UserNameLength = SamAccountName.Length;
RtlCopyMemory(dataptr, SamAccountName.Buffer, length);
dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -