Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
9.4 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // lockkey.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines the class LockoutKey.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 10/21/1998 Original version.
  16. // 11/04/1998 Fix bug in computing key expiration.
  17. // 01/14/1999 Move initialization code out of constructor.
  18. //
  19. ///////////////////////////////////////////////////////////////////////////////
  20. #include <ias.h>
  21. #include <lm.h>
  22. #include <lockkey.h>
  23. #include <yvals.h>
  24. //////////
  25. // Registry key and value names.
  26. //////////
  27. const WCHAR KEY_NAME_ACCOUNT_LOCKOUT[] = L"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess\\Parameters\\AccountLockout";
  28. const WCHAR VALUE_NAME_LOCKOUT_COUNT[] = L"MaxDenials";
  29. const WCHAR VALUE_NAME_RESET_TIME[] = L"ResetTime (mins)";
  30. //////////
  31. // Registry value defaults.
  32. //////////
  33. const DWORD DEFAULT_LOCKOUT_COUNT = 0;
  34. const DWORD DEFAULT_RESET_TIME = 48 * 60; // 48 hours.
  35. /////////
  36. // Helper function that reads a DWORD registry value. If the value isn't set
  37. // or is corrupt, then a default value is written to the registry.
  38. /////////
  39. DWORD
  40. WINAPI
  41. RegQueryDWORDWithDefault(
  42. HKEY hKey,
  43. PCWSTR lpValueName,
  44. DWORD dwDefault
  45. )
  46. {
  47. LONG result;
  48. DWORD type, data, cb;
  49. cb = sizeof(DWORD);
  50. result = RegQueryValueExW(
  51. hKey,
  52. lpValueName,
  53. NULL,
  54. &type,
  55. (LPBYTE)&data,
  56. &cb
  57. );
  58. if (result == NO_ERROR && type == REG_DWORD && cb == sizeof(DWORD))
  59. {
  60. return data;
  61. }
  62. if (result == NO_ERROR || result == ERROR_FILE_NOT_FOUND)
  63. {
  64. RegSetValueExW(
  65. hKey,
  66. lpValueName,
  67. 0,
  68. REG_DWORD,
  69. (CONST BYTE*)&dwDefault,
  70. sizeof(DWORD)
  71. );
  72. }
  73. return dwDefault;
  74. }
  75. LockoutKey::LockoutKey() throw ()
  76. : maxDenials(DEFAULT_LOCKOUT_COUNT),
  77. refCount(0),
  78. hLockout(NULL),
  79. hChangeEvent(NULL),
  80. hRegisterWait(NULL),
  81. ttl(DEFAULT_RESET_TIME),
  82. lastCollection(0)
  83. { }
  84. void LockoutKey::initialize() throw ()
  85. {
  86. IASGlobalLockSentry sentry;
  87. if (refCount == 0)
  88. {
  89. // Create or open the lockout key.
  90. LONG result;
  91. DWORD disposition;
  92. result = RegCreateKeyEx(
  93. HKEY_LOCAL_MACHINE,
  94. KEY_NAME_ACCOUNT_LOCKOUT,
  95. NULL,
  96. NULL,
  97. REG_OPTION_NON_VOLATILE,
  98. KEY_ALL_ACCESS,
  99. NULL,
  100. &hLockout,
  101. &disposition
  102. );
  103. // Event used for signalling changes to the registry.
  104. hChangeEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
  105. // Register for change notifications.
  106. RegNotifyChangeKeyValue(
  107. hLockout,
  108. FALSE,
  109. REG_NOTIFY_CHANGE_LAST_SET,
  110. hChangeEvent,
  111. TRUE
  112. );
  113. // Read the initial values.
  114. readValues();
  115. // Register the event.
  116. RegisterWaitForSingleObject(
  117. &hRegisterWait,
  118. hChangeEvent,
  119. onChange,
  120. this,
  121. INFINITE,
  122. 0
  123. );
  124. }
  125. ++refCount;
  126. }
  127. void LockoutKey::finalize() throw ()
  128. {
  129. IASGlobalLockSentry sentry;
  130. if (--refCount == 0)
  131. {
  132. UnregisterWait(hRegisterWait);
  133. if (hLockout) { RegCloseKey(hLockout); }
  134. CloseHandle(hChangeEvent);
  135. }
  136. }
  137. HKEY LockoutKey::createEntry(PCWSTR subKeyName) throw ()
  138. {
  139. HKEY hKey = NULL;
  140. DWORD disposition;
  141. RegCreateKeyEx(
  142. hLockout,
  143. subKeyName,
  144. NULL,
  145. NULL,
  146. REG_OPTION_NON_VOLATILE,
  147. KEY_ALL_ACCESS,
  148. NULL,
  149. &hKey,
  150. &disposition
  151. );
  152. // Whenever we grow the registry, we'll also clean up old entries.
  153. if (ttl) { collectGarbage(); }
  154. return hKey;
  155. }
  156. HKEY LockoutKey::openEntry(PCWSTR subKeyName) throw ()
  157. {
  158. LONG result;
  159. HKEY hKey = NULL;
  160. result = RegOpenKeyEx(
  161. hLockout,
  162. subKeyName,
  163. 0,
  164. KEY_ALL_ACCESS,
  165. &hKey
  166. );
  167. if (result == NO_ERROR && ttl)
  168. {
  169. // We retrieved a key, but we need to make sure it hasn't expired.
  170. ULARGE_INTEGER lastWritten;
  171. result = RegQueryInfoKey(
  172. hKey,
  173. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  174. (LPFILETIME)&lastWritten
  175. );
  176. if (result == NO_ERROR)
  177. {
  178. ULARGE_INTEGER now;
  179. GetSystemTimeAsFileTime((LPFILETIME)&now);
  180. if (now.QuadPart - lastWritten.QuadPart >= ttl)
  181. {
  182. // It's expired, so close the key ...
  183. RegCloseKey(hKey);
  184. hKey = NULL;
  185. // ... and delete.
  186. deleteEntry(subKeyName);
  187. }
  188. }
  189. }
  190. return hKey;
  191. }
  192. void LockoutKey::clear() throw ()
  193. {
  194. // Get the number of sub-keys.
  195. LONG result;
  196. DWORD index;
  197. result = RegQueryInfoKey(
  198. hLockout,
  199. NULL, NULL, NULL,
  200. &index,
  201. NULL, NULL, NULL, NULL, NULL, NULL, NULL
  202. );
  203. if (result != NO_ERROR) { return; }
  204. // Iterate through the keys in reverse order so we can delete them
  205. // without throwing off the indices.
  206. while (index)
  207. {
  208. --index;
  209. WCHAR name[DNLEN + UNLEN + 2];
  210. DWORD cbName = sizeof(name) / sizeof(WCHAR);
  211. result = RegEnumKeyEx(
  212. hLockout,
  213. index,
  214. name,
  215. &cbName,
  216. NULL,
  217. NULL,
  218. NULL,
  219. NULL
  220. );
  221. if (result == NO_ERROR) { RegDeleteKey(hLockout, name); }
  222. }
  223. }
  224. void LockoutKey::collectGarbage() throw ()
  225. {
  226. // Flag that indicates whether another thread is collecting.
  227. static LONG inProgress;
  228. // Save the TTL to a local variable, so we don't have to worry about it
  229. // changing will we're executing.
  230. ULONGLONG localTTL = ttl;
  231. // If the reset time is not configured, then bail.
  232. if (localTTL == 0) { return; }
  233. // We won't collect more frequently than the TTL.
  234. ULARGE_INTEGER now;
  235. GetSystemTimeAsFileTime((LPFILETIME)&now);
  236. if (now.QuadPart - lastCollection < localTTL) { return; }
  237. // If another thread is alreay collecting, then bail.
  238. if (InterlockedExchange(&inProgress, 1)) { return; }
  239. // Save the new collection time.
  240. lastCollection = now.QuadPart;
  241. // Get the number of sub-keys.
  242. LONG result;
  243. DWORD index;
  244. result = RegQueryInfoKey(
  245. hLockout,
  246. NULL, NULL, NULL,
  247. &index,
  248. NULL, NULL, NULL, NULL, NULL, NULL, NULL
  249. );
  250. if (result == NO_ERROR)
  251. {
  252. // We iterate through the keys in reverse order so we can delete them
  253. // without throwing off the indices.
  254. while (index)
  255. {
  256. --index;
  257. // Get the lastWritten time for the key ...
  258. WCHAR name[DNLEN + UNLEN + 2];
  259. DWORD cbName = sizeof(name) / sizeof(WCHAR);
  260. ULARGE_INTEGER lastWritten;
  261. result = RegEnumKeyEx(
  262. hLockout,
  263. index,
  264. name,
  265. &cbName,
  266. NULL,
  267. NULL,
  268. NULL,
  269. (LPFILETIME)&lastWritten
  270. );
  271. // ... and delete if it's expired.
  272. if (result == NO_ERROR &&
  273. now.QuadPart - lastWritten.QuadPart >= localTTL)
  274. {
  275. RegDeleteKey(hLockout, name);
  276. }
  277. }
  278. }
  279. // Collection is no longer in progress.
  280. InterlockedExchange(&inProgress, 0);
  281. }
  282. void LockoutKey::readValues() throw ()
  283. {
  284. /////////
  285. // Note: This isn't synchronized. The side-effects of an inconsistent state
  286. // are pretty minor, so we'll just take our chances.
  287. /////////
  288. // Read max. denials.
  289. maxDenials = RegQueryDWORDWithDefault(
  290. hLockout,
  291. VALUE_NAME_LOCKOUT_COUNT,
  292. DEFAULT_LOCKOUT_COUNT
  293. );
  294. // Read Time-To-Live.
  295. ULONGLONG newTTL = RegQueryDWORDWithDefault(
  296. hLockout,
  297. VALUE_NAME_RESET_TIME,
  298. DEFAULT_RESET_TIME
  299. );
  300. newTTL *= 600000000ui64;
  301. ttl = newTTL;
  302. if (maxDenials == 0)
  303. {
  304. // If account lockout is disabled, clean up all the keys.
  305. clear();
  306. }
  307. else
  308. {
  309. // Otherwise, the TTL may have changed, so collect garbage.
  310. collectGarbage();
  311. }
  312. }
  313. VOID NTAPI LockoutKey::onChange(PVOID context, BOOLEAN flag) throw ()
  314. {
  315. // Re-read the values.
  316. ((LockoutKey*)context)->readValues();
  317. // Re-register the notification.
  318. RegNotifyChangeKeyValue(
  319. ((LockoutKey*)context)->hLockout,
  320. FALSE,
  321. REG_NOTIFY_CHANGE_LAST_SET,
  322. ((LockoutKey*)context)->hChangeEvent,
  323. TRUE
  324. );
  325. }