Source code of Windows XP (NT5)
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.

824 lines
19 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. adtlog.c
  5. Abstract:
  6. Auditing - Audit Record Queuing and Logging Routines
  7. This file contains functions that construct Audit Records in self-
  8. relative form from supplied information, enqueue/dequeue them and
  9. write them to the log.
  10. Author:
  11. Scott Birrell (ScottBi) November 8, 1991
  12. Environment:
  13. Kernel Mode only
  14. Revision History:
  15. --*/
  16. #include "pch.h"
  17. #pragma hdrstop
  18. #ifdef ALLOC_PRAGMA
  19. #pragma alloc_text(PAGE,SepAdtLogAuditRecord)
  20. #pragma alloc_text(PAGE,SepAuditFailed)
  21. #pragma alloc_text(PAGE,SepAdtMarshallAuditRecord)
  22. #pragma alloc_text(PAGE,SepAdtSetAuditLogInformation)
  23. #pragma alloc_text(PAGE,SepAdtCopyToLsaSharedMemory)
  24. #pragma alloc_text(PAGE,SepQueueWorkItem)
  25. #pragma alloc_text(PAGE,SepDequeueWorkItem)
  26. #endif
  27. VOID
  28. SepAdtLogAuditRecord(
  29. IN PSE_ADT_PARAMETER_ARRAY AuditParameters
  30. )
  31. /*++
  32. Routine Description:
  33. This function manages the logging of Audit Records. It provides the
  34. single interface to the Audit Logging component from the Audit/Alarm
  35. generation routines. The function constructs an Audit Record in
  36. self-relative format from the information provided and appends it to
  37. the Audit Record Queue, a doubly-linked list of Audit Records awaiting
  38. output to the Audit Log. A dedicated thread reads this queue, writing
  39. Audit Records to the Audit Log and removing them from the Audit Queue.
  40. Arguments:
  41. AuditEventType - Specifies the type of the Audit Event described by
  42. the audit information provided.
  43. AuditInformation - Pointer to buffer containing captured auditing
  44. information related to an Audit Event of type AuditEventType.
  45. Return Value:
  46. STATUS_SUCCESS
  47. STATUS_UNSUCCESSFUL - Audit record was not queued
  48. STATUS_INSUFFICIENT_RESOURCES - unable to allocate heap
  49. --*/
  50. {
  51. NTSTATUS Status;
  52. PSEP_LSA_WORK_ITEM AuditWorkItem;
  53. PAGED_CODE();
  54. AuditWorkItem = ExAllocatePoolWithTag( PagedPool, sizeof( SEP_LSA_WORK_ITEM ), 'iAeS' );
  55. if ( AuditWorkItem == NULL ) {
  56. SepAuditFailed();
  57. return;
  58. }
  59. AuditWorkItem->Tag = SepAuditRecord;
  60. AuditWorkItem->CommandNumber = LsapWriteAuditMessageCommand;
  61. AuditWorkItem->ReplyBuffer = NULL;
  62. AuditWorkItem->ReplyBufferLength = 0;
  63. AuditWorkItem->CleanupFunction = NULL;
  64. //
  65. // Build an Audit record in self-relative format from the supplied
  66. // Audit Information.
  67. //
  68. Status = SepAdtMarshallAuditRecord(
  69. AuditParameters,
  70. (PSE_ADT_PARAMETER_ARRAY *) &AuditWorkItem->CommandParams.BaseAddress,
  71. &AuditWorkItem->CommandParamsMemoryType
  72. );
  73. if (NT_SUCCESS(Status)) {
  74. //
  75. // Extract the length of the Audit Record. Store it as the length
  76. // of the Command Parameters buffer.
  77. //
  78. AuditWorkItem->CommandParamsLength =
  79. ((PSE_ADT_PARAMETER_ARRAY) AuditWorkItem->CommandParams.BaseAddress)->Length;
  80. //
  81. // If we're going to crash on a discarded audit, ignore the queue bounds
  82. // check and force the item onto the queue.
  83. //
  84. if (!SepQueueWorkItem( AuditWorkItem, (BOOLEAN)(SepCrashOnAuditFail ? TRUE : FALSE) )) {
  85. ExFreePool( AuditWorkItem->CommandParams.BaseAddress );
  86. ExFreePool( AuditWorkItem );
  87. //
  88. // We failed to put the record on the queue. Take whatever action is
  89. // appropriate.
  90. //
  91. SepAuditFailed();
  92. }
  93. } else {
  94. ExFreePool( AuditWorkItem );
  95. SepAuditFailed();
  96. }
  97. }
  98. VOID
  99. SepAuditFailed(
  100. VOID
  101. )
  102. /*++
  103. Routine Description:
  104. Bugchecks the system due to a missed audit (optional requirement
  105. for C2 compliance).
  106. Arguments:
  107. None.
  108. Return Value:
  109. None.
  110. --*/
  111. {
  112. NTSTATUS Status;
  113. OBJECT_ATTRIBUTES Obja;
  114. HANDLE KeyHandle;
  115. UNICODE_STRING KeyName;
  116. UNICODE_STRING ValueName;
  117. UCHAR NewValue;
  118. ASSERT(sizeof(UCHAR) == sizeof(BOOLEAN));
  119. if (!SepCrashOnAuditFail) {
  120. return;
  121. }
  122. //
  123. // Turn off flag in the registry that controls crashing on audit failure
  124. //
  125. RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
  126. InitializeObjectAttributes( &Obja,
  127. &KeyName,
  128. OBJ_CASE_INSENSITIVE |
  129. OBJ_KERNEL_HANDLE,
  130. NULL,
  131. NULL
  132. );
  133. do {
  134. Status = ZwOpenKey(
  135. &KeyHandle,
  136. KEY_SET_VALUE,
  137. &Obja
  138. );
  139. } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
  140. //
  141. // If the LSA key isn't there, he's got big problems. But don't crash.
  142. //
  143. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  144. SepCrashOnAuditFail = FALSE;
  145. return;
  146. }
  147. if (!NT_SUCCESS( Status )) {
  148. goto bugcheck;
  149. }
  150. RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
  151. NewValue = LSAP_ALLOW_ADIMIN_LOGONS_ONLY;
  152. do {
  153. Status = ZwSetValueKey( KeyHandle,
  154. &ValueName,
  155. 0,
  156. REG_NONE,
  157. &NewValue,
  158. sizeof(UCHAR)
  159. );
  160. } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
  161. ASSERT(NT_SUCCESS(Status));
  162. if (!NT_SUCCESS( Status )) {
  163. goto bugcheck;
  164. }
  165. do {
  166. Status = ZwFlushKey( KeyHandle );
  167. } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
  168. ASSERT(NT_SUCCESS(Status));
  169. //
  170. // go boom.
  171. //
  172. bugcheck:
  173. KeBugCheckEx(AUDIT_FAILURE, 0, 0, 0, 0);
  174. }
  175. NTSTATUS
  176. SepAdtMarshallAuditRecord(
  177. IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
  178. OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters,
  179. OUT PSEP_RM_LSA_MEMORY_TYPE RecordMemoryType
  180. )
  181. /*++
  182. Routine Description:
  183. This routine will take an AuditParamters structure and create
  184. a new AuditParameters structure that is suitable for sending
  185. to LSA. It will be in self-relative form and allocated as
  186. a single chunk of memory.
  187. Arguments:
  188. AuditParameters - A filled in set of AuditParameters to be marshalled.
  189. MarshalledAuditParameters - Returns a pointer to a block of heap memory
  190. containing the passed AuditParameters in self-relative form suitable
  191. for passing to LSA.
  192. Return Value:
  193. None.
  194. --*/
  195. {
  196. ULONG i;
  197. ULONG TotalSize = sizeof( SE_ADT_PARAMETER_ARRAY );
  198. PUNICODE_STRING TargetString;
  199. PCHAR Base;
  200. ULONG BaseIncr;
  201. ULONG Size;
  202. PSE_ADT_PARAMETER_ARRAY_ENTRY pInParam, pOutParam;
  203. PAGED_CODE();
  204. //
  205. // Calculate the total size required for the passed AuditParameters
  206. // block. This calculation will probably be an overestimate of the
  207. // amount of space needed, because data smaller that 2 dwords will
  208. // be stored directly in the parameters structure, but their length
  209. // will be counted here anyway. The overestimate can't be more than
  210. // 24 dwords, and will never even approach that amount, so it isn't
  211. // worth the time it would take to avoid it.
  212. //
  213. for (i=0; i<AuditParameters->ParameterCount; i++) {
  214. Size = AuditParameters->Parameters[i].Length;
  215. TotalSize += PtrAlignSize( Size );
  216. }
  217. //
  218. // Allocate a big enough block of memory to hold everything.
  219. // If it fails, quietly abort, since there isn't much else we
  220. // can do.
  221. //
  222. *MarshalledAuditParameters = ExAllocatePoolWithTag( PagedPool, TotalSize, 'pAeS' );
  223. if (*MarshalledAuditParameters == NULL) {
  224. *RecordMemoryType = SepRmNoMemory;
  225. return(STATUS_INSUFFICIENT_RESOURCES);
  226. }
  227. *RecordMemoryType = SepRmPagedPoolMemory;
  228. RtlCopyMemory (
  229. *MarshalledAuditParameters,
  230. AuditParameters,
  231. sizeof( SE_ADT_PARAMETER_ARRAY )
  232. );
  233. (*MarshalledAuditParameters)->Length = TotalSize;
  234. (*MarshalledAuditParameters)->Flags = SE_ADT_PARAMETERS_SELF_RELATIVE;
  235. pInParam = &AuditParameters->Parameters[0];
  236. pOutParam = &((*MarshalledAuditParameters)->Parameters[0]);
  237. //
  238. // Start walking down the list of parameters and marshall them
  239. // into the target buffer.
  240. //
  241. Base = (PCHAR) ((PCHAR)(*MarshalledAuditParameters) + sizeof( SE_ADT_PARAMETER_ARRAY ));
  242. for (i=0; i<AuditParameters->ParameterCount; i++, pInParam++, pOutParam++) {
  243. switch (AuditParameters->Parameters[i].Type) {
  244. case SeAdtParmTypeNone:
  245. case SeAdtParmTypeUlong:
  246. case SeAdtParmTypeLogonId:
  247. case SeAdtParmTypeNoLogonId:
  248. case SeAdtParmTypeTime:
  249. case SeAdtParmTypeAccessMask:
  250. case SeAdtParmTypePtr:
  251. {
  252. //
  253. // Nothing to do for this
  254. //
  255. break;
  256. }
  257. case SeAdtParmTypeString:
  258. case SeAdtParmTypeFileSpec:
  259. {
  260. PUNICODE_STRING SourceString;
  261. //
  262. // We must copy the body of the unicode string
  263. // and then copy the body of the string. Pointers
  264. // must be turned into offsets.
  265. TargetString = (PUNICODE_STRING)Base;
  266. SourceString = pInParam->Address;
  267. *TargetString = *SourceString;
  268. //
  269. // Reset the data pointer in the output parameters to
  270. // 'point' to the new string structure.
  271. //
  272. pOutParam->Address = Base - (ULONG_PTR)(*MarshalledAuditParameters);
  273. Base += sizeof( UNICODE_STRING );
  274. RtlCopyMemory( Base, SourceString->Buffer, SourceString->Length );
  275. //
  276. // Make the string buffer in the target string point to where we
  277. // just copied the data.
  278. //
  279. TargetString->Buffer = (PWSTR)(Base - (ULONG_PTR)(*MarshalledAuditParameters));
  280. BaseIncr = PtrAlignSize(SourceString->Length);
  281. Base += BaseIncr;
  282. ASSERT( (ULONG_PTR)Base <= (ULONG_PTR)(*MarshalledAuditParameters) + TotalSize );
  283. break;
  284. }
  285. //
  286. // Handle types where we simply copy the buffer.
  287. //
  288. case SeAdtParmTypePrivs:
  289. // #if DBG
  290. // {
  291. // PPRIVILEGE_SET Privileges =
  292. // (PPRIVILEGE_SET) pInParam->Address;
  293. // ULONG i;
  294. // for (i = 0; i < Privileges->PrivilegeCount; i++)
  295. // {
  296. // ASSERT( Privileges->Privilege[i].Luid.HighPart == 0);
  297. // }
  298. // }
  299. // #endif
  300. case SeAdtParmTypeSid:
  301. case SeAdtParmTypeObjectTypes:
  302. {
  303. //
  304. // Copy the data into the output buffer
  305. //
  306. RtlCopyMemory( Base, pInParam->Address, pInParam->Length );
  307. //
  308. // Reset the 'address' of the data to be its offset in the
  309. // buffer.
  310. //
  311. pOutParam->Address = Base - (ULONG_PTR)(*MarshalledAuditParameters);
  312. Base += PtrAlignSize( pInParam->Length );
  313. ASSERT( (ULONG_PTR)Base <= (ULONG_PTR)(*MarshalledAuditParameters) + TotalSize );
  314. break;
  315. }
  316. default:
  317. {
  318. //
  319. // We got passed junk, complain.
  320. //
  321. ASSERT( FALSE );
  322. break;
  323. }
  324. }
  325. }
  326. return( STATUS_SUCCESS );
  327. }
  328. VOID
  329. SepAdtSetAuditLogInformation(
  330. IN PPOLICY_AUDIT_LOG_INFO AuditLogInformation
  331. )
  332. /*++
  333. Routine Description:
  334. This function stores Audit Log Information in the Reference Monitor's
  335. in-memory database. This information contains parameters such as Audit
  336. Log size etc. It is the caller's responsibility to ensure that the
  337. supplied information is valid.
  338. NOTE: After initialization, this function is only called by the LSA
  339. via a Reference Monitor command. This is a necessary restriction
  340. because the Audit Log Information stored in the LSA Database must
  341. remain in sync
  342. Arguments:
  343. AuditLogInformation - Pointer to Audit Log Information structure.
  344. Return Value:
  345. None.
  346. --*/
  347. {
  348. PAGED_CODE();
  349. //
  350. // Acquire Reference Monitor Database write lock.
  351. //
  352. SepRmAcquireDbWriteLock();
  353. //
  354. // Write the information
  355. //
  356. SepAdtLogInformation = *AuditLogInformation;
  357. //
  358. // Release Reference Monitor Database write lock
  359. //
  360. SepRmReleaseDbWriteLock();
  361. }
  362. NTSTATUS
  363. SepAdtCopyToLsaSharedMemory(
  364. IN HANDLE LsaProcessHandle,
  365. IN PVOID Buffer,
  366. IN ULONG BufferLength,
  367. OUT PVOID *LsaBufferAddress
  368. )
  369. /*++
  370. Routine Description:
  371. This function allocates memory shared with the LSA and optionally copies
  372. a given buffer to it.
  373. Arguments:
  374. LsaProcessHandle - Specifies a handle to the Lsa Process.
  375. Buffer - Pointer to the buffer to be copied.
  376. BufferLength - Length of buffer.
  377. LsaBufferAddress - Receives the address of the buffer valid in the
  378. Lsa process context.
  379. Return Value:
  380. NTSTATUS - Standard Nt Result Code
  381. Result codes returned by called routines.
  382. --*/
  383. {
  384. NTSTATUS Status, SecondaryStatus;
  385. PVOID OutputLsaBufferAddress = NULL;
  386. SIZE_T RegionSize = BufferLength;
  387. PAGED_CODE();
  388. Status = ZwAllocateVirtualMemory(
  389. LsaProcessHandle,
  390. &OutputLsaBufferAddress,
  391. 0,
  392. &RegionSize,
  393. MEM_COMMIT,
  394. PAGE_READWRITE
  395. );
  396. if (!NT_SUCCESS(Status)) {
  397. goto CopyToLsaSharedMemoryError;
  398. }
  399. Status = ZwWriteVirtualMemory(
  400. LsaProcessHandle,
  401. OutputLsaBufferAddress,
  402. Buffer,
  403. BufferLength,
  404. NULL
  405. );
  406. if (!NT_SUCCESS(Status)) {
  407. goto CopyToLsaSharedMemoryError;
  408. }
  409. *LsaBufferAddress = OutputLsaBufferAddress;
  410. return(Status);
  411. CopyToLsaSharedMemoryError:
  412. //
  413. // If we allocated memory, free it.
  414. //
  415. if (OutputLsaBufferAddress != NULL) {
  416. RegionSize = 0;
  417. SecondaryStatus = ZwFreeVirtualMemory(
  418. LsaProcessHandle,
  419. &OutputLsaBufferAddress,
  420. &RegionSize,
  421. MEM_RELEASE
  422. );
  423. ASSERT(NT_SUCCESS(SecondaryStatus));
  424. OutputLsaBufferAddress = NULL;
  425. }
  426. return(Status);
  427. }
  428. BOOLEAN
  429. SepQueueWorkItem(
  430. IN PSEP_LSA_WORK_ITEM LsaWorkItem,
  431. IN BOOLEAN ForceQueue
  432. )
  433. /*++
  434. Routine Description:
  435. Puts the passed work item on the queue to be passed to LSA,
  436. and returns the state of the queue upon arrival.
  437. Arguments:
  438. LsaWorkItem - Pointer to the work item to be queued.
  439. ForceQueue - Indicate that this item is not to be discarded
  440. because of a full queue.
  441. Return Value:
  442. TRUE - The item was successfully queued.
  443. FALSE - The item was not queued and must be discarded.
  444. --*/
  445. {
  446. BOOLEAN rc = TRUE;
  447. BOOLEAN StartExThread = FALSE ;
  448. PAGED_CODE();
  449. #if 0
  450. DbgPrint("Queueing an audit\n");
  451. #endif
  452. SepLockLsaQueue();
  453. //
  454. // See if LSA has died. If it has then just return with an error.
  455. //
  456. if (SepAdtLsaDeadEvent != NULL) {
  457. rc = FALSE;
  458. goto Exit;
  459. }
  460. if (SepAdtDiscardingAudits && !ForceQueue) {
  461. if (SepAdtCurrentListLength < SepAdtMinListLength) {
  462. //
  463. // We need to generate an audit saying how many audits we've
  464. // discarded.
  465. //
  466. // Since we have the mutex protecting the Audit queue, we don't
  467. // have to worry about anyone coming along and logging an
  468. // audit. But *we* can, since a mutex may be acquired recursively.
  469. //
  470. // Since we are so protected, turn off the SepAdtDiscardingAudits
  471. // flag here so that we don't come through this path again.
  472. //
  473. SepAdtDiscardingAudits = FALSE;
  474. SepAdtGenerateDiscardAudit();
  475. #if 0
  476. DbgPrint("Auditing resumed\n");
  477. #endif
  478. //
  479. // We must assume that that worked, so clear the discard count.
  480. //
  481. SepAdtCountEventsDiscarded = 0;
  482. //
  483. // Our 'audits discarded' audit is now on the queue,
  484. // continue logging the one we started with.
  485. //
  486. } else {
  487. //
  488. // We are not yet below our low water mark. Toss
  489. // this audit and increment the discard count.
  490. //
  491. SepAdtCountEventsDiscarded++;
  492. rc = FALSE;
  493. goto Exit;
  494. }
  495. }
  496. if (SepAdtCurrentListLength < SepAdtMaxListLength || ForceQueue) {
  497. InsertTailList(&SepLsaQueue, &LsaWorkItem->List);
  498. if (++SepAdtCurrentListLength == 1) {
  499. #if 0
  500. DbgPrint("Queueing a work item\n");
  501. #endif
  502. StartExThread = TRUE ;
  503. }
  504. } else {
  505. //
  506. // There is no room for this audit on the queue,
  507. // so change our state to 'discarding' and tell
  508. // the caller to toss this audit.
  509. //
  510. SepAdtDiscardingAudits = TRUE;
  511. #if 0
  512. DbgPrint("Starting to discard audits\n");
  513. #endif
  514. rc = FALSE;
  515. }
  516. Exit:
  517. SepUnlockLsaQueue();
  518. if ( StartExThread )
  519. {
  520. ExInitializeWorkItem( &SepExWorkItem.WorkItem,
  521. (PWORKER_THREAD_ROUTINE) SepRmCallLsa,
  522. &SepExWorkItem
  523. );
  524. ExQueueWorkItem( &SepExWorkItem.WorkItem, DelayedWorkQueue );
  525. }
  526. return( rc );
  527. }
  528. PSEP_LSA_WORK_ITEM
  529. SepDequeueWorkItem(
  530. VOID
  531. )
  532. /*++
  533. Routine Description:
  534. Removes the top element of the SepLsaQueue and returns the
  535. next element if there is one, NULL otherwise.
  536. Arguments:
  537. None.
  538. Return Value:
  539. A pointer to the next SEP_LSA_WORK_ITEM, or NULL.
  540. --*/
  541. {
  542. PSEP_LSA_WORK_ITEM OldWorkQueueItem;
  543. PAGED_CODE();
  544. SepLockLsaQueue();
  545. OldWorkQueueItem = (PSEP_LSA_WORK_ITEM)RemoveHeadList(&SepLsaQueue);
  546. OldWorkQueueItem->List.Flink = NULL;
  547. SepAdtCurrentListLength--;
  548. #if 0
  549. DbgPrint("Removing item\n");
  550. #endif
  551. if (IsListEmpty( &SepLsaQueue )) {
  552. //
  553. // If LSA has died and the RM thread is waiting till we finish up. Notify it that we are all done
  554. //
  555. if (SepAdtLsaDeadEvent != NULL) {
  556. KeSetEvent (SepAdtLsaDeadEvent, 0, FALSE);
  557. }
  558. SepUnlockLsaQueue();
  559. ExFreePool( OldWorkQueueItem );
  560. return( NULL );
  561. }
  562. //
  563. // We know there's something on the queue now, so we
  564. // can unlock it.
  565. //
  566. SepUnlockLsaQueue();
  567. ExFreePool( OldWorkQueueItem );
  568. return((PSEP_LSA_WORK_ITEM)(&SepLsaQueue)->Flink);
  569. }