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.

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