|
|
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
//
// FILE
//
// lockkey.cpp
//
// SYNOPSIS
//
// Defines the class LockoutKey.
//
// MODIFICATION HISTORY
//
// 10/21/1998 Original version.
// 11/04/1998 Fix bug in computing key expiration.
// 01/14/1999 Move initialization code out of constructor.
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <lm.h>
#include <lockkey.h>
#include <yvals.h>
//////////
// Registry key and value names.
//////////
const WCHAR KEY_NAME_ACCOUNT_LOCKOUT[] = L"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess\\Parameters\\AccountLockout"; const WCHAR VALUE_NAME_LOCKOUT_COUNT[] = L"MaxDenials"; const WCHAR VALUE_NAME_RESET_TIME[] = L"ResetTime (mins)";
//////////
// Registry value defaults.
//////////
const DWORD DEFAULT_LOCKOUT_COUNT = 0; const DWORD DEFAULT_RESET_TIME = 48 * 60; // 48 hours.
/////////
// Helper function that reads a DWORD registry value. If the value isn't set
// or is corrupt, then a default value is written to the registry.
/////////
DWORD WINAPI RegQueryDWORDWithDefault( HKEY hKey, PCWSTR lpValueName, DWORD dwDefault ) { LONG result; DWORD type, data, cb;
cb = sizeof(DWORD); result = RegQueryValueExW( hKey, lpValueName, NULL, &type, (LPBYTE)&data, &cb );
if (result == NO_ERROR && type == REG_DWORD && cb == sizeof(DWORD)) { return data; }
if (result == NO_ERROR || result == ERROR_FILE_NOT_FOUND) { RegSetValueExW( hKey, lpValueName, 0, REG_DWORD, (CONST BYTE*)&dwDefault, sizeof(DWORD) ); }
return dwDefault; }
LockoutKey::LockoutKey() throw () : maxDenials(DEFAULT_LOCKOUT_COUNT), refCount(0), hLockout(NULL), hChangeEvent(NULL), hRegisterWait(NULL), ttl(DEFAULT_RESET_TIME), lastCollection(0) { }
void LockoutKey::initialize() throw () { IASGlobalLockSentry sentry;
if (refCount == 0) { // Create or open the lockout key.
LONG result; DWORD disposition; result = RegCreateKeyEx( HKEY_LOCAL_MACHINE, KEY_NAME_ACCOUNT_LOCKOUT, NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hLockout, &disposition );
// Event used for signalling changes to the registry.
hChangeEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
// Register for change notifications.
RegNotifyChangeKeyValue( hLockout, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hChangeEvent, TRUE );
// Read the initial values.
readValues();
// Register the event.
RegisterWaitForSingleObject( &hRegisterWait, hChangeEvent, onChange, this, INFINITE, 0 ); }
++refCount; }
void LockoutKey::finalize() throw () { IASGlobalLockSentry sentry;
if (--refCount == 0) { UnregisterWait(hRegisterWait);
if (hLockout) { RegCloseKey(hLockout); }
CloseHandle(hChangeEvent); } }
HKEY LockoutKey::createEntry(PCWSTR subKeyName) throw () { HKEY hKey = NULL; DWORD disposition; RegCreateKeyEx( hLockout, subKeyName, NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &disposition );
// Whenever we grow the registry, we'll also clean up old entries.
if (ttl) { collectGarbage(); }
return hKey; }
HKEY LockoutKey::openEntry(PCWSTR subKeyName) throw () { LONG result; HKEY hKey = NULL; result = RegOpenKeyEx( hLockout, subKeyName, 0, KEY_ALL_ACCESS, &hKey );
if (result == NO_ERROR && ttl) { // We retrieved a key, but we need to make sure it hasn't expired.
ULARGE_INTEGER lastWritten; result = RegQueryInfoKey( hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, (LPFILETIME)&lastWritten ); if (result == NO_ERROR) { ULARGE_INTEGER now; GetSystemTimeAsFileTime((LPFILETIME)&now);
if (now.QuadPart - lastWritten.QuadPart >= ttl) { // It's expired, so close the key ...
RegCloseKey(hKey); hKey = NULL;
// ... and delete.
deleteEntry(subKeyName); } } }
return hKey; }
void LockoutKey::clear() throw () { // Get the number of sub-keys.
LONG result; DWORD index; result = RegQueryInfoKey( hLockout, NULL, NULL, NULL, &index, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); if (result != NO_ERROR) { return; }
// Iterate through the keys in reverse order so we can delete them
// without throwing off the indices.
while (index) { --index;
WCHAR name[DNLEN + UNLEN + 2]; DWORD cbName = sizeof(name) / sizeof(WCHAR); result = RegEnumKeyEx( hLockout, index, name, &cbName, NULL, NULL, NULL, NULL );
if (result == NO_ERROR) { RegDeleteKey(hLockout, name); } } }
void LockoutKey::collectGarbage() throw () { // Flag that indicates whether another thread is collecting.
static LONG inProgress;
// Save the TTL to a local variable, so we don't have to worry about it
// changing will we're executing.
ULONGLONG localTTL = ttl;
// If the reset time is not configured, then bail.
if (localTTL == 0) { return; }
// We won't collect more frequently than the TTL.
ULARGE_INTEGER now; GetSystemTimeAsFileTime((LPFILETIME)&now); if (now.QuadPart - lastCollection < localTTL) { return; }
// If another thread is alreay collecting, then bail.
if (InterlockedExchange(&inProgress, 1)) { return; }
// Save the new collection time.
lastCollection = now.QuadPart;
// Get the number of sub-keys.
LONG result; DWORD index; result = RegQueryInfoKey( hLockout, NULL, NULL, NULL, &index, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); if (result == NO_ERROR) { // We iterate through the keys in reverse order so we can delete them
// without throwing off the indices.
while (index) { --index;
// Get the lastWritten time for the key ...
WCHAR name[DNLEN + UNLEN + 2]; DWORD cbName = sizeof(name) / sizeof(WCHAR); ULARGE_INTEGER lastWritten; result = RegEnumKeyEx( hLockout, index, name, &cbName, NULL, NULL, NULL, (LPFILETIME)&lastWritten );
// ... and delete if it's expired.
if (result == NO_ERROR && now.QuadPart - lastWritten.QuadPart >= localTTL) { RegDeleteKey(hLockout, name); } } }
// Collection is no longer in progress.
InterlockedExchange(&inProgress, 0); }
void LockoutKey::readValues() throw () { /////////
// Note: This isn't synchronized. The side-effects of an inconsistent state
// are pretty minor, so we'll just take our chances.
/////////
// Read max. denials.
maxDenials = RegQueryDWORDWithDefault( hLockout, VALUE_NAME_LOCKOUT_COUNT, DEFAULT_LOCKOUT_COUNT );
// Read Time-To-Live.
ULONGLONG newTTL = RegQueryDWORDWithDefault( hLockout, VALUE_NAME_RESET_TIME, DEFAULT_RESET_TIME ); newTTL *= 600000000ui64; ttl = newTTL;
if (maxDenials == 0) { // If account lockout is disabled, clean up all the keys.
clear(); } else { // Otherwise, the TTL may have changed, so collect garbage.
collectGarbage(); } }
VOID NTAPI LockoutKey::onChange(PVOID context, BOOLEAN flag) throw () { // Re-read the values.
((LockoutKey*)context)->readValues();
// Re-register the notification.
RegNotifyChangeKeyValue( ((LockoutKey*)context)->hLockout, FALSE, REG_NOTIFY_CHANGE_LAST_SET, ((LockoutKey*)context)->hChangeEvent, TRUE ); }
|