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.

892 lines
22 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. adtinit.c
  5. Abstract:
  6. Local Security Authority - Auditing Initialization
  7. Author:
  8. Scott Birrell (ScottBi) November 20, 1991
  9. Environment:
  10. Revision History:
  11. --*/
  12. #include <lsapch2.h>
  13. #include "adtp.h"
  14. NTSTATUS LsapAdtInitializeCrashOnFail( VOID );
  15. //
  16. // Array of drive letter to device mappings for generating path strings.
  17. //
  18. DRIVE_MAPPING DriveMappingArray[MAX_DRIVE_MAPPING];
  19. //
  20. // Name that will be used as the default subsystem name for LSA generated events
  21. //
  22. UNICODE_STRING LsapSubsystemName;
  23. //
  24. // Name that will be passed in for SubsystemName for some audits generated
  25. // by LSA for LSA objects.
  26. //
  27. UNICODE_STRING LsapLsaName;
  28. //
  29. // Special privilege values which are not normally audited,
  30. // but generate audits when assigned to a user. See
  31. // LsapAdtAuditSpecialPrivileges.
  32. //
  33. LUID ChangeNotifyPrivilege;
  34. LUID AuditPrivilege;
  35. LUID CreateTokenPrivilege;
  36. LUID AssignPrimaryTokenPrivilege;
  37. LUID BackupPrivilege;
  38. LUID RestorePrivilege;
  39. LUID DebugPrivilege;
  40. //
  41. // Global variable indicating whether or not we are supposed
  42. // to crash when an audit fails.
  43. //
  44. BOOLEAN LsapCrashOnAuditFail = FALSE;
  45. BOOLEAN LsapAllowAdminLogonsOnly = FALSE;
  46. NTSTATUS
  47. LsapAdtInitialize(
  48. )
  49. /*++
  50. Routine Description:
  51. This function performs initialization of auditing within the LSA, and
  52. it also issues commands to the Reference Monitor to enable it to
  53. complete any initialization of auditing variables that is dependent
  54. on the content of the LSA Database. At time of call, the main
  55. System Init thread is in the Reference Monitor awaiting completion
  56. of all LSA initialization, and the Reference Monitor Command
  57. Server thread is waiting for commands.
  58. The following steps are performed:
  59. o Read the Audit Event and Audit Log information from the LSA
  60. Database.
  61. o Call the Event Logging function to open the Audit Log
  62. o Issue a Reference Monitor command to write the Audit Event Info
  63. to the Reference-Monitor's in-memory database.
  64. Arguments:
  65. None
  66. Return Value:
  67. NTSTATUS - Standard Nt Result Code.
  68. All Result Codes are generated by called routines.
  69. --*/
  70. {
  71. NTSTATUS Status = STATUS_SUCCESS;
  72. ULONG AuditLogInfoLength = sizeof (POLICY_AUDIT_LOG_INFO);
  73. ULONG AuditEventInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
  74. UNICODE_STRING UnicodeString;
  75. PUNICODE_STRING Strings;
  76. LSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo;
  77. Strings = &UnicodeString;
  78. RtlInitUnicodeString( Strings, L"System Restart");
  79. RtlInitUnicodeString( &LsapSubsystemName, L"Security" );
  80. RtlInitUnicodeString( &LsapLsaName, L"LSA" );
  81. //
  82. // initialize debug helper support. this function
  83. // does nothing for free builds
  84. //
  85. LsapAdtInitDebug();
  86. //
  87. // init LsapCrashOnAuditFail global var so that we can crash
  88. // if any of the following initialization fails
  89. //
  90. (VOID) LsapAdtInitializeCrashOnFail();
  91. Status = LsapAdtInitGenericAudits();
  92. if (!NT_SUCCESS(Status)) {
  93. goto AuditInitError;
  94. }
  95. Status = LsapAdtInitializeExtensibleAuditing();
  96. if (!NT_SUCCESS(Status)) {
  97. goto AuditInitError;
  98. }
  99. Status = LsapAdtInitializeLogQueue();
  100. if (!NT_SUCCESS(Status)) {
  101. goto AuditInitError;
  102. }
  103. Status = LsapAdtInitializePerUserAuditing();
  104. if (!NT_SUCCESS(Status)) {
  105. LsapLogError("LsapAdtInitialize: LsapAdtInitializePerUserAuditing() returned 0x%lx\n",
  106. Status);
  107. goto AuditInitError;
  108. }
  109. //
  110. // Read the Audit Log Information from the PolAdtLg attribute of the Lsa
  111. // Database object.
  112. //
  113. Status = LsapDbReadAttributeObject(
  114. LsapDbHandle,
  115. &LsapDbNames[PolAdtLg],
  116. &LsapAdtLogInformation,
  117. &AuditLogInfoLength
  118. );
  119. if (!NT_SUCCESS(Status)) {
  120. LsapLogError(
  121. "LsapAdtInitialize: Read Audit Log Info returned 0x%lx\n",
  122. Status
  123. );
  124. goto AuditInitError;
  125. }
  126. //
  127. // Read the Audit Event Information from the AdtEvent attribute of the Lsa
  128. // Database object. The information consists of the Auditing Mode and
  129. // the Auditing Options for each Audit Event Type.
  130. //
  131. Status = LsapDbReadAttributeObject(
  132. LsapDbHandle,
  133. &LsapDbNames[PolAdtEv],
  134. &AuditEventsInfo,
  135. &AuditEventInfoLength
  136. );
  137. if (!NT_SUCCESS(Status)) {
  138. //
  139. // This section of code is temporary and allows an old
  140. // Policy Database to work with the new Audit Event Categories
  141. // without the need to re-install. The Audit Event Information
  142. // is overwritten with the new format and all auditing is turned
  143. // off.
  144. //
  145. if (Status == STATUS_BUFFER_OVERFLOW) {
  146. KdPrint(("LsapAdtInitialize: Old Audit Event Info detected\n"
  147. "Replacing with new format, all auditing disabled\n"));
  148. //
  149. // Initialize Default Event Auditing Options. No auditing is specified
  150. // for any event type.
  151. //
  152. Status = LsapAdtInitializeDefaultAuditing(
  153. LSAP_DB_UPDATE_POLICY_DATABASE,
  154. &AuditEventsInfo
  155. );
  156. if (!NT_SUCCESS(Status)) {
  157. goto AuditInitError;
  158. }
  159. } else {
  160. LsapLogError(
  161. "LsapAdtInitialize: Read Audit Event Info returned 0x%lx\n",
  162. Status
  163. );
  164. goto AuditInitError;
  165. }
  166. }
  167. //
  168. // update the LSA global var that holds audit policy
  169. //
  170. RtlCopyMemory(
  171. &LsapAdtEventsInformation,
  172. &AuditEventsInfo,
  173. sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO)
  174. );
  175. //
  176. // generate SE_AUDITID_SYSTEM_RESTART
  177. //
  178. LsapAdtSystemRestart( &AuditEventsInfo );
  179. //
  180. // Send a command to the Reference Monitor to write the Auditing
  181. // State to its in-memory data.
  182. //
  183. Status = LsapCallRm(
  184. RmAuditSetCommand,
  185. &AuditEventsInfo,
  186. sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
  187. NULL,
  188. 0
  189. );
  190. if (!NT_SUCCESS(Status)) {
  191. LsapLogError("LsapAdtInitialize: LsapCallRm returned 0x%lx\n", Status);
  192. goto AuditInitError;
  193. }
  194. Status = LsapAdtInitializeDriveLetters();
  195. if (!NT_SUCCESS(Status)) {
  196. LsapLogError("LsapAdtInitialize: LsapAdtInitializeDriveLetters() returned 0x%lx\n",
  197. Status);
  198. goto AuditInitError;
  199. }
  200. //
  201. // Initialize privilege values we need
  202. //
  203. ChangeNotifyPrivilege = RtlConvertLongToLuid( SE_CHANGE_NOTIFY_PRIVILEGE );
  204. AuditPrivilege = RtlConvertLongToLuid( SE_AUDIT_PRIVILEGE );
  205. CreateTokenPrivilege = RtlConvertLongToLuid( SE_CREATE_TOKEN_PRIVILEGE );
  206. AssignPrimaryTokenPrivilege = RtlConvertLongToLuid( SE_ASSIGNPRIMARYTOKEN_PRIVILEGE );
  207. BackupPrivilege = RtlConvertLongToLuid( SE_BACKUP_PRIVILEGE );
  208. RestorePrivilege = RtlConvertLongToLuid( SE_RESTORE_PRIVILEGE );
  209. DebugPrivilege = RtlConvertLongToLuid( SE_DEBUG_PRIVILEGE );
  210. AuditInitFinish:
  211. return(Status);
  212. AuditInitError:
  213. //
  214. // raise harderror if LsapCrashOnAuditFail is TRUE
  215. //
  216. LsapAuditFailed( Status );
  217. goto AuditInitFinish;
  218. }
  219. NTSTATUS
  220. LsapAdtInitializeDefaultAuditing(
  221. IN ULONG Options,
  222. OUT PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInformation
  223. )
  224. /*++
  225. Routine Description:
  226. This routine sets an initial default Auditing State in which auditing
  227. is turned off. It is called only during initialization of the LSA
  228. or during the installation of its Policy Database. The initial
  229. auditing state may also optionally be written to the Lsa Policy
  230. Database provided that the Policy Object has been created and its
  231. internal handle is available.
  232. Arguments:
  233. Options - Specifies optional actions to be taken
  234. LSAP_DB_UPDATE_POLICY_DATABASE - Update the corresponding information
  235. in the Policy Database. This option must only be specified
  236. where it is known that the Policy Object exists.
  237. AuditEventsInformation - Pointer to structure that will receive the Audit Event
  238. Information
  239. Return Values:
  240. None.
  241. --*/
  242. {
  243. NTSTATUS Status = STATUS_SUCCESS;
  244. LSAP_DB_ATTRIBUTE AuditEventsAttribute;
  245. BOOLEAN ObjectReferenced = FALSE;
  246. ULONG EventAuditingOptionsLength =
  247. (POLICY_AUDIT_EVENT_TYPE_COUNT * sizeof(POLICY_AUDIT_EVENT_OPTIONS));
  248. //
  249. // Turn off auditing and set the count of Audit Event Types (Categories)
  250. //
  251. AuditEventsInformation->AuditingMode = FALSE;
  252. AuditEventsInformation->MaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;
  253. //
  254. // Turn off auditing for all events.
  255. //
  256. RtlZeroMemory(AuditEventsInformation->EventAuditingOptions, EventAuditingOptionsLength);
  257. if (Options & LSAP_DB_UPDATE_POLICY_DATABASE) {
  258. ASSERT(LsapPolicyHandle != NULL);
  259. //
  260. // Start a transaction on the Policy Object
  261. //
  262. Status = LsapDbReferenceObject(
  263. LsapPolicyHandle,
  264. (ACCESS_MASK) 0,
  265. PolicyObject,
  266. PolicyObject,
  267. LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION
  268. );
  269. if (!NT_SUCCESS(Status)) {
  270. goto InitializeDefaultAuditingError;
  271. }
  272. ObjectReferenced = TRUE;
  273. LsapDbInitializeAttribute(
  274. &AuditEventsAttribute,
  275. &LsapDbNames[PolAdtEv],
  276. AuditEventsInformation,
  277. sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
  278. FALSE
  279. );
  280. Status = LsapDbWriteAttributesObject(
  281. LsapPolicyHandle,
  282. &AuditEventsAttribute,
  283. (ULONG) 1
  284. );
  285. if (!NT_SUCCESS(Status)) {
  286. goto InitializeDefaultAuditingError;
  287. }
  288. }
  289. InitializeDefaultAuditingFinish:
  290. if (ObjectReferenced) {
  291. Status = LsapDbDereferenceObject(
  292. LsapPolicyHandle,
  293. PolicyObject,
  294. PolicyObject,
  295. LSAP_DB_LOCK | LSAP_DB_FINISH_TRANSACTION,
  296. (SECURITY_DB_DELTA_TYPE) 0,
  297. Status
  298. );
  299. ObjectReferenced = FALSE;
  300. }
  301. return(Status);
  302. InitializeDefaultAuditingError:
  303. goto InitializeDefaultAuditingFinish;
  304. }
  305. NTSTATUS
  306. LsapAdtInitializePerUserAuditing(
  307. VOID
  308. )
  309. /*++
  310. Routine Description:
  311. Initializes the per user auditing hash table and reads data from the
  312. registry.
  313. Arguments:
  314. None.
  315. Return Value:
  316. NTSTATUS
  317. --*/
  318. {
  319. NTSTATUS Status = STATUS_SUCCESS;
  320. BOOLEAN bSuccess;
  321. BOOLEAN bLock = FALSE;
  322. PVOID pNotificationItem = NULL;
  323. RtlInitializeResource(&LsapAdtPerUserPolicyTableResource);
  324. RtlInitializeResource(&LsapAdtPerUserLuidTableResource);
  325. LsapAdtPerUserKeyEvent = CreateEvent(
  326. NULL,
  327. FALSE,
  328. FALSE,
  329. NULL
  330. );
  331. ASSERT(LsapAdtPerUserKeyEvent);
  332. if (!LsapAdtPerUserKeyEvent)
  333. {
  334. Status = LsapWinerrorToNtStatus(GetLastError());
  335. goto Cleanup;
  336. }
  337. LsapAdtPerUserKeyTimer = CreateWaitableTimer(
  338. NULL,
  339. FALSE,
  340. NULL
  341. );
  342. ASSERT(LsapAdtPerUserKeyTimer);
  343. if (!LsapAdtPerUserKeyTimer)
  344. {
  345. Status = LsapWinerrorToNtStatus(GetLastError());
  346. goto Cleanup;
  347. }
  348. bSuccess = LsapAdtAcquirePerUserPolicyTableWriteLock();
  349. ASSERT(bSuccess);
  350. if (!bSuccess)
  351. {
  352. Status = STATUS_UNSUCCESSFUL;
  353. goto Cleanup;
  354. }
  355. bLock = TRUE;
  356. Status = LsapAdtConstructTablePerUserAuditing();
  357. if (!NT_SUCCESS(Status))
  358. {
  359. ASSERT(L"Failed to construct per user auditing table." && FALSE);
  360. goto Cleanup;
  361. }
  362. //
  363. // Now register for changes to the key, so that we can rebuild the
  364. // table to reflect current policy. The event is signalled by
  365. // registry change. The timer is set by the NotifyStub routine.
  366. //
  367. pNotificationItem =
  368. LsaIRegisterNotification(
  369. (LPTHREAD_START_ROUTINE)LsapAdtKeyNotifyStubPerUserAuditing,
  370. 0,
  371. NOTIFIER_TYPE_HANDLE_WAIT,
  372. 0,
  373. 0,
  374. 0,
  375. LsapAdtPerUserKeyEvent
  376. );
  377. if ( !pNotificationItem )
  378. {
  379. Status = STATUS_INSUFFICIENT_RESOURCES;
  380. goto Cleanup;
  381. }
  382. pNotificationItem =
  383. LsaIRegisterNotification(
  384. (LPTHREAD_START_ROUTINE)LsapAdtKeyNotifyFirePerUserAuditing,
  385. 0,
  386. NOTIFIER_TYPE_HANDLE_WAIT,
  387. 0,
  388. 0,
  389. 0,
  390. LsapAdtPerUserKeyTimer
  391. );
  392. if ( !pNotificationItem )
  393. {
  394. Status = STATUS_INSUFFICIENT_RESOURCES;
  395. goto Cleanup;
  396. }
  397. Cleanup:
  398. if (bLock)
  399. {
  400. LsapAdtReleasePerUserPolicyTableLock();
  401. }
  402. if (!NT_SUCCESS(Status))
  403. {
  404. LsapAuditFailed(Status);
  405. }
  406. return Status;
  407. }
  408. NTSTATUS
  409. LsapAdtInitializeDriveLetters(
  410. VOID
  411. )
  412. /*++
  413. Routine Description:
  414. Initializes an array of symbolic link to drive letter mappings
  415. for use by auditing code.
  416. Arguments:
  417. None.
  418. Return Value:
  419. NTSTATUS - currently either STATUS_SUCCESS or STATUS_NO_MEMORY.
  420. --*/
  421. {
  422. UNICODE_STRING LinkName;
  423. PUNICODE_STRING DeviceName;
  424. OBJECT_ATTRIBUTES Obja;
  425. HANDLE LinkHandle;
  426. NTSTATUS Status = STATUS_SUCCESS;
  427. ULONG i;
  428. PWCHAR p;
  429. PWCHAR DeviceNameBuffer;
  430. ULONG MappingIndex = 0;
  431. WCHAR wszDosDevices[sizeof(L"\\DosDevices\\A:") + 1];
  432. wcscpy(wszDosDevices, L"\\DosDevices\\A:");
  433. RtlInitUnicodeString(&LinkName, wszDosDevices);
  434. p = (PWCHAR)LinkName.Buffer;
  435. //
  436. // Make p point to the drive letter in the LinkName string
  437. //
  438. p = p+12;
  439. for( i=0 ; i<26 ; i++ ){
  440. *p = (WCHAR)'A' + (WCHAR)i;
  441. InitializeObjectAttributes(
  442. &Obja,
  443. &LinkName,
  444. OBJ_CASE_INSENSITIVE,
  445. NULL,
  446. NULL
  447. );
  448. Status = NtOpenSymbolicLinkObject(
  449. &LinkHandle,
  450. SYMBOLIC_LINK_QUERY,
  451. &Obja
  452. );
  453. if (NT_SUCCESS( Status )) {
  454. //
  455. // Open succeeded, Now get the link value
  456. //
  457. DriveMappingArray[MappingIndex].DriveLetter = *p;
  458. DeviceName = &DriveMappingArray[MappingIndex].DeviceName;
  459. DeviceNameBuffer = LsapAllocateLsaHeap( MAXIMUM_FILENAME_LENGTH );
  460. //
  461. // if LsapAllocateLsaHeap can't get any memory then return
  462. //
  463. if (DeviceNameBuffer != NULL) {
  464. DeviceName->Length = 0;
  465. DeviceName->MaximumLength = MAXIMUM_FILENAME_LENGTH;
  466. DeviceName->Buffer = DeviceNameBuffer;
  467. Status = NtQuerySymbolicLinkObject(
  468. LinkHandle,
  469. DeviceName,
  470. NULL
  471. );
  472. NtClose(LinkHandle);
  473. if ( NT_SUCCESS(Status) ) {
  474. MappingIndex++;
  475. } else {
  476. LsapFreeLsaHeap( DeviceNameBuffer );
  477. RtlInitUnicodeString( DeviceName, NULL );
  478. }
  479. } else {
  480. Status = STATUS_NO_MEMORY;
  481. break; // since couldn't alloc mem, get out of the for loop and return
  482. }
  483. }
  484. }
  485. //
  486. // Now we know all drive letters that map to devices. However, at this point,
  487. // some of the 'devices' that the letters link to may themselves be symbolic
  488. // links (ie nested symbolic links). We must check for this and find the actual
  489. // underlying object. Only perform the nested symlink search if we did not run
  490. // out of memory above.
  491. //
  492. if (Status != STATUS_NO_MEMORY) {
  493. BOOLEAN bLinkIsNested = FALSE;
  494. NTSTATUS NestedSearchStatus = STATUS_SUCCESS;
  495. i = 0;
  496. while (i < MappingIndex) {
  497. bLinkIsNested = FALSE;
  498. DeviceName = &DriveMappingArray[i].DeviceName;
  499. InitializeObjectAttributes(
  500. &Obja,
  501. DeviceName,
  502. OBJ_CASE_INSENSITIVE,
  503. NULL,
  504. NULL
  505. );
  506. NestedSearchStatus = NtOpenSymbolicLinkObject(
  507. &LinkHandle,
  508. SYMBOLIC_LINK_QUERY,
  509. &Obja
  510. );
  511. if (NT_SUCCESS( NestedSearchStatus )) {
  512. //
  513. // The open succeeded, so DeviceName was actually a nested symbolic link.
  514. //
  515. NestedSearchStatus = NtQuerySymbolicLinkObject(
  516. LinkHandle,
  517. DeviceName,
  518. NULL
  519. );
  520. if (NT_SUCCESS( NestedSearchStatus )) {
  521. bLinkIsNested = TRUE;
  522. } else {
  523. //
  524. // If the query fails, then free the buffer and move on.
  525. //
  526. LsapFreeLsaHeap( DeviceName->Buffer );
  527. RtlInitUnicodeString( DeviceName, NULL );
  528. }
  529. NtClose(LinkHandle);
  530. } else if (NestedSearchStatus == STATUS_OBJECT_TYPE_MISMATCH) {
  531. //
  532. // NtOpenSymbolicLinkObject failed with object type mismatch. Good. We
  533. // have reached the actual device in the nested links.
  534. //
  535. } else {
  536. #if DBG
  537. DbgPrint("NtQuerySymbolicLinkObject on handle 0x%x returned 0x%x\n", LinkHandle, NestedSearchStatus);
  538. #endif
  539. ASSERT("NtQuerySymbolicLinkObject failed with unexpected status." && FALSE);
  540. }
  541. if (!bLinkIsNested) {
  542. //
  543. // Move on to the next drive letter.
  544. //
  545. i++;
  546. }
  547. }
  548. }
  549. //
  550. // one of two values should be return. STATUS_NO_MEMORY should be returned
  551. // if LsapAllocateLsaHeap() fails; STATUS_SUCCESS returns in all other
  552. // cases. this test must be made because Status may contain a different
  553. // value after the return of NtOpenSymbolicLinkObject() or
  554. // NtQuerySymbolicLinkObject(). if either of those functions fail,
  555. // LsapAdtInitializeDriveLetters() should still return STATUS_SUCCESS.
  556. //
  557. if (Status == STATUS_NO_MEMORY) {
  558. return Status;
  559. }
  560. return STATUS_SUCCESS;
  561. }
  562. NTSTATUS
  563. LsapAdtInitializeCrashOnFail(
  564. VOID
  565. )
  566. /*++
  567. Routine Description:
  568. Reads the registry to see if the user has told us to crash if an audit fails.
  569. Arguments:
  570. None.
  571. Return Value:
  572. STATUS_SUCCESS
  573. --*/
  574. {
  575. HANDLE KeyHandle;
  576. NTSTATUS Status;
  577. NTSTATUS TmpStatus;
  578. OBJECT_ATTRIBUTES Obja;
  579. ULONG ResultLength;
  580. UNICODE_STRING KeyName;
  581. UNICODE_STRING ValueName;
  582. CHAR KeyInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BOOLEAN)];
  583. PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
  584. //
  585. // Check the value of the CrashOnAudit key.
  586. //
  587. RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
  588. InitializeObjectAttributes( &Obja,
  589. &KeyName,
  590. OBJ_CASE_INSENSITIVE,
  591. NULL,
  592. NULL
  593. );
  594. Status = NtOpenKey(
  595. &KeyHandle,
  596. KEY_QUERY_VALUE | KEY_SET_VALUE,
  597. &Obja
  598. );
  599. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  600. LsapCrashOnAuditFail = FALSE;
  601. return( STATUS_SUCCESS );
  602. }
  603. RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
  604. Status = NtQueryValueKey(
  605. KeyHandle,
  606. &ValueName,
  607. KeyValuePartialInformation,
  608. KeyInfo,
  609. sizeof(KeyInfo),
  610. &ResultLength
  611. );
  612. TmpStatus = NtClose(KeyHandle);
  613. ASSERT(NT_SUCCESS(TmpStatus));
  614. //
  615. // If it's not found, don't enable CrashOnFail.
  616. //
  617. if (!NT_SUCCESS( Status )) {
  618. LsapCrashOnAuditFail = FALSE;
  619. } else {
  620. //
  621. // Check the value of the CrashOnFail value. If it is 1, we
  622. // crash on audit fail. If it is two, we only allow admins to
  623. // logon.
  624. //
  625. pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;
  626. if (*(pKeyInfo->Data) == LSAP_CRASH_ON_AUDIT_FAIL) {
  627. LsapCrashOnAuditFail = TRUE;
  628. } else if (*(pKeyInfo->Data) == LSAP_ALLOW_ADIMIN_LOGONS_ONLY) {
  629. LsapAllowAdminLogonsOnly = TRUE;
  630. }
  631. }
  632. return( STATUS_SUCCESS );
  633. }