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.

3212 lines
81 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. Utility routines for sac driver
  7. Author:
  8. Andrew Ritz (andrewr) - 15 June, 2000
  9. Revision History:
  10. added new utils: Brian Guarraci (briangu) - 2001
  11. --*/
  12. #include "sac.h"
  13. #include <guiddef.h>
  14. VOID
  15. AppendMessage(
  16. PWSTR OutPutBuffer,
  17. ULONG MessageId,
  18. PWSTR ValueBuffer OPTIONAL
  19. );
  20. NTSTATUS
  21. InsertRegistrySzIntoMachineInfoBuffer(
  22. PWSTR KeyName,
  23. PWSTR ValueName,
  24. ULONG MessageId
  25. );
  26. #ifdef ALLOC_PRAGMA
  27. #pragma alloc_text( INIT, PreloadGlobalMessageTable )
  28. #pragma alloc_text( INIT, AppendMessage )
  29. #pragma alloc_text( INIT, InsertRegistrySzIntoMachineInfoBuffer )
  30. #pragma alloc_text( INIT, InitializeMachineInformation )
  31. #endif
  32. //
  33. // (see comments in sac.h)
  34. //
  35. PUCHAR Utf8ConversionBuffer;
  36. ULONG Utf8ConversionBufferSize = MEMORY_INCREMENT;
  37. WCHAR IncomingUnicodeValue;
  38. UCHAR IncomingUtf8ConversionBuffer[3];
  39. //
  40. // Message Table routines. We load all of our message table entries into a
  41. // global non-paged structure so that we can send text to HeadlessDispatch at
  42. // any time.
  43. //
  44. typedef struct _MESSAGE_TABLE_ENTRY {
  45. ULONG MessageId;
  46. PCWSTR MessageText;
  47. } MESSAGE_TABLE_ENTRY, *PMESSAGE_TABLE_ENTRY;
  48. PMESSAGE_TABLE_ENTRY GlobalMessageTable;
  49. ULONG GlobalMessageTableCount;
  50. #define MESSAGE_INITIAL 1
  51. #define MESSAGE_FINAL 200
  52. //
  53. // Prototypes
  54. //
  55. extern
  56. BOOLEAN
  57. ExVerifySuite(
  58. SUITE_TYPE SuiteType
  59. );
  60. ULONG
  61. ConvertAnsiToUnicode(
  62. OUT PWSTR pwch,
  63. IN PSTR pch,
  64. IN ULONG cchMax
  65. )
  66. /*++
  67. Routine Description:
  68. Convert an ansi character string into unicode.
  69. Arguments:
  70. pwch - the unicode string
  71. pch - the ansi string
  72. cchMax - the max length to copy (including null termination)
  73. Return Value:
  74. # of characters converted (not including null termination)
  75. --*/
  76. {
  77. ULONG Count;
  78. ASSERT_STATUS(pch, 0);
  79. ASSERT_STATUS(pwch, 0);
  80. Count = 0;
  81. while ((*pch != '\0') && (Count < (cchMax-1))) {
  82. *pwch = (WCHAR)(*pch);
  83. pwch++;
  84. pch++;
  85. Count++;
  86. }
  87. *pwch = UNICODE_NULL;
  88. return Count;
  89. }
  90. NTSTATUS
  91. RegisterSacCmdEvent(
  92. IN PFILE_OBJECT FileObject,
  93. IN PSAC_CMD_SETUP_CMD_EVENT SetupCmdEvent
  94. )
  95. /*++
  96. Routine Description:
  97. This routine populates the sac cmd event info specified by
  98. the user-mode service responsible for responding to requests
  99. to launch a command console session.
  100. Arguments:
  101. FileObject - the FileObject ptr for the driver handle object
  102. used by the registering process
  103. SetupCmdEvent - the event info
  104. Return Value:
  105. Status
  106. Security:
  107. Interface:
  108. external --> internal
  109. this routine does not prevent reregistration of the cmd event info
  110. this behavior should be handled by the caller
  111. --*/
  112. {
  113. NTSTATUS Status;
  114. BOOLEAN b;
  115. ASSERT_STATUS(SetupCmdEvent, STATUS_INVALID_PARAMETER_1);
  116. //
  117. // Protect the SAC Cmd Event Info
  118. //
  119. KeWaitForMutexObject(
  120. &SACCmdEventInfoMutex,
  121. Executive,
  122. KernelMode,
  123. FALSE,
  124. NULL
  125. );
  126. do {
  127. //
  128. // make sure there isn't a service already regiseterd
  129. //
  130. if (UserModeServiceHasRegisteredCmdEvent()) {
  131. Status = STATUS_UNSUCCESSFUL;
  132. break;
  133. }
  134. //
  135. // Reset our info to the initial condition
  136. //
  137. // Note: this cleans up the cmd event info if present
  138. //
  139. InitializeCmdEventInfo();
  140. #if ENABLE_SERVICE_FILE_OBJECT_CHECKING
  141. //
  142. // get a reference to the registering process's driver handle
  143. // file object so we can make sure that an unregister IOCTL
  144. // comes from the same process.
  145. //
  146. Status = ObReferenceObjectByPointer(
  147. FileObject,
  148. GENERIC_READ,
  149. *IoFileObjectType,
  150. KernelMode
  151. );
  152. if (!NT_SUCCESS(Status)) {
  153. break;
  154. }
  155. ServiceProcessFileObject = FileObject;
  156. #else
  157. UNREFERENCED_PARAMETER(FileObject);
  158. #endif
  159. //
  160. // test and aqcuire the RequestSacCmdEvent event handle
  161. //
  162. b = VerifyEventWaitable(
  163. SetupCmdEvent->RequestSacCmdEvent,
  164. &RequestSacCmdEventObjectBody,
  165. &RequestSacCmdEventWaitObjectBody
  166. );
  167. if(!b) {
  168. Status = STATUS_INVALID_HANDLE;
  169. break;
  170. }
  171. //
  172. // test and aqcuire the RequestSacCmdSuccessEvent event handle
  173. //
  174. b = VerifyEventWaitable(
  175. SetupCmdEvent->RequestSacCmdSuccessEvent,
  176. &RequestSacCmdSuccessEventObjectBody,
  177. &RequestSacCmdSuccessEventWaitObjectBody
  178. );
  179. if(!b) {
  180. Status = STATUS_INVALID_HANDLE;
  181. ObDereferenceObject(RequestSacCmdEventObjectBody);
  182. break;
  183. }
  184. //
  185. // test and aqcuire the RequestSacCmdFailureEvent event handle
  186. //
  187. b = VerifyEventWaitable(
  188. SetupCmdEvent->RequestSacCmdFailureEvent,
  189. &RequestSacCmdFailureEventObjectBody,
  190. &RequestSacCmdFailureEventWaitObjectBody
  191. );
  192. if(!b) {
  193. Status = STATUS_INVALID_HANDLE;
  194. ObDereferenceObject(RequestSacCmdEventObjectBody);
  195. ObDereferenceObject(RequestSacCmdSuccessEventWaitObjectBody);
  196. break;
  197. }
  198. //
  199. // declare that we indeed have the user-mode service info
  200. //
  201. HaveUserModeServiceCmdEventInfo = TRUE;
  202. //
  203. // We have successfully registered teh SAC Cmd Event Info
  204. //
  205. Status = STATUS_SUCCESS;
  206. } while (FALSE);
  207. KeReleaseMutex(&SACCmdEventInfoMutex, FALSE);
  208. return Status;
  209. }
  210. #if ENABLE_SERVICE_FILE_OBJECT_CHECKING
  211. BOOLEAN
  212. IsCmdEventRegistrationProcess(
  213. IN PFILE_OBJECT FileObject
  214. )
  215. /*++
  216. Routine Description:
  217. This routine purges the previously registered sac cmd event info.
  218. Note: This should be called when the user-mode service shuts down.
  219. Arguments:
  220. FileObject - the FileObject ptr for the driver handle object
  221. used by the registering process
  222. Return Value:
  223. Status
  224. --*/
  225. {
  226. BOOLEAN bIsRegistrationProcess;
  227. //
  228. // Default
  229. //
  230. bIsRegistrationProcess = FALSE;
  231. //
  232. // Protect the SAC Cmd Event Info
  233. //
  234. KeWaitForMutexObject(
  235. &SACCmdEventInfoMutex,
  236. Executive,
  237. KernelMode,
  238. FALSE,
  239. NULL
  240. );
  241. do {
  242. //
  243. // exit if there is no service registered
  244. //
  245. if (! UserModeServiceHasRegisteredCmdEvent()) {
  246. break;
  247. }
  248. //
  249. // make sure the calling process is the same
  250. // that registered
  251. //
  252. if (FileObject == ServiceProcessFileObject) {
  253. bIsRegistrationProcess = TRUE;
  254. break;
  255. }
  256. } while (FALSE);
  257. KeReleaseMutex(&SACCmdEventInfoMutex, FALSE);
  258. return bIsRegistrationProcess;
  259. }
  260. #endif
  261. NTSTATUS
  262. UnregisterSacCmdEvent(
  263. IN PFILE_OBJECT FileObject
  264. )
  265. /*++
  266. Routine Description:
  267. This routine purges the previously registered sac cmd event info.
  268. Note: This should be called when the user-mode service shuts down.
  269. Arguments:
  270. FileObject - the FileObject ptr for the driver handle object
  271. used by the registering process
  272. Return Value:
  273. Status
  274. --*/
  275. {
  276. NTSTATUS Status;
  277. //
  278. // Protect the SAC Cmd Event Info
  279. //
  280. KeWaitForMutexObject(
  281. &SACCmdEventInfoMutex,
  282. Executive,
  283. KernelMode,
  284. FALSE,
  285. NULL
  286. );
  287. do {
  288. //
  289. // exit if there is no service registered
  290. //
  291. if (! UserModeServiceHasRegisteredCmdEvent()) {
  292. Status = STATUS_UNSUCCESSFUL;
  293. break;
  294. }
  295. #if ENABLE_SERVICE_FILE_OBJECT_CHECKING
  296. //
  297. // make sure the calling process is the same
  298. // that registered
  299. //
  300. if (FileObject != ServiceProcessFileObject) {
  301. Status = STATUS_UNSUCCESSFUL;
  302. break;
  303. }
  304. //
  305. // since we are unregistering,
  306. // we no longer need to hold a reference to
  307. // the driver handle object
  308. //
  309. ObDereferenceObject(FileObject);
  310. #else
  311. UNREFERENCED_PARAMETER(FileObject);
  312. #endif
  313. //
  314. // Reset our info to the initial condition
  315. //
  316. InitializeCmdEventInfo();
  317. Status = STATUS_SUCCESS;
  318. } while (FALSE);
  319. KeReleaseMutex(&SACCmdEventInfoMutex, FALSE);
  320. return Status;
  321. }
  322. VOID
  323. InitializeCmdEventInfo(
  324. VOID
  325. )
  326. /*++
  327. Routine Description:
  328. Initialize the Cmd Console Event Information.
  329. Arguments:
  330. NONE
  331. Return Value:
  332. NONE
  333. --*/
  334. {
  335. //
  336. // Dereference the wait objects if we have them
  337. //
  338. if (HaveUserModeServiceCmdEventInfo) {
  339. ASSERT(RequestSacCmdEventObjectBody);
  340. ASSERT(RequestSacCmdSuccessEventObjectBody);
  341. ASSERT(RequestSacCmdFailureEventObjectBody);
  342. if (RequestSacCmdEventObjectBody) {
  343. ObDereferenceObject(RequestSacCmdEventObjectBody);
  344. }
  345. if (RequestSacCmdSuccessEventObjectBody) {
  346. ObDereferenceObject(RequestSacCmdSuccessEventObjectBody);
  347. }
  348. if (RequestSacCmdFailureEventObjectBody) {
  349. ObDereferenceObject(RequestSacCmdFailureEventObjectBody);
  350. }
  351. }
  352. //
  353. // reset the cmd console event information
  354. //
  355. RequestSacCmdEventObjectBody = NULL;
  356. RequestSacCmdEventWaitObjectBody = NULL;
  357. RequestSacCmdSuccessEventObjectBody = NULL;
  358. RequestSacCmdSuccessEventWaitObjectBody = NULL;
  359. RequestSacCmdFailureEventObjectBody = NULL;
  360. RequestSacCmdFailureEventWaitObjectBody = NULL;
  361. #if ENABLE_SERVICE_FILE_OBJECT_CHECKING
  362. //
  363. // reset the process file object ptr
  364. //
  365. ServiceProcessFileObject = NULL;
  366. #endif
  367. //
  368. // declare that we do NOT have the user-mode service info
  369. //
  370. HaveUserModeServiceCmdEventInfo = FALSE;
  371. }
  372. BOOLEAN
  373. VerifyEventWaitable(
  374. IN HANDLE hEvent,
  375. OUT PVOID *EventObjectBody,
  376. OUT PVOID *EventWaitObjectBody
  377. )
  378. /*++
  379. Routine Description:
  380. This routine extracts the waitable object from the
  381. specified event object. It also verifies that there
  382. is a waitable object present.
  383. Note: if successful, this routine returns with the
  384. reference count incremented on the event object.
  385. The caller is responsible for releasing this
  386. object.
  387. Arguments:
  388. hEvent - The handle to the event object
  389. EventObjectBody - The event object
  390. EventWaitObjectBody - The waitiable object
  391. Return Value:
  392. TRUE - event is waitable
  393. FALSE - otherwise
  394. Security:
  395. This routine operates on event objects referred by event handles from
  396. usermode.
  397. --*/
  398. {
  399. POBJECT_HEADER ObjectHeader;
  400. NTSTATUS Status;
  401. //
  402. // Reference the event and verify that it is waitable.
  403. //
  404. Status = ObReferenceObjectByHandle(
  405. hEvent,
  406. EVENT_ALL_ACCESS,
  407. NULL,
  408. KernelMode,
  409. EventObjectBody,
  410. NULL
  411. );
  412. if(!NT_SUCCESS(Status)) {
  413. IF_SAC_DEBUG(
  414. SAC_DEBUG_FAILS,
  415. KdPrint(("SAC VerifyEventWaitable: Unable to reference event object (%lx)\n",Status))
  416. );
  417. return(FALSE);
  418. }
  419. ObjectHeader = OBJECT_TO_OBJECT_HEADER(*EventObjectBody);
  420. if(!ObjectHeader->Type->TypeInfo.UseDefaultObject) {
  421. *EventWaitObjectBody = (PVOID)((PCHAR)(*EventObjectBody) +
  422. (ULONG_PTR)ObjectHeader->Type->DefaultObject);
  423. } else {
  424. IF_SAC_DEBUG(
  425. SAC_DEBUG_FAILS,
  426. KdPrint(("SAC VerifyEventWaitable: event object not waitable!\n"))
  427. );
  428. ObDereferenceObject(*EventObjectBody);
  429. return(FALSE);
  430. }
  431. return(TRUE);
  432. }
  433. NTSTATUS
  434. InvokeUserModeService(
  435. VOID
  436. )
  437. /*++
  438. Routine Description:
  439. This routine manages the interaction with the user-mode service responsible
  440. for launching the cmd console channel.
  441. Arguments:
  442. None
  443. Return Value:
  444. STATUS_SUCCESS - if cmd console was successfully launched by user-mode service
  445. otherwise, error status
  446. --*/
  447. {
  448. NTSTATUS Status;
  449. LARGE_INTEGER TimeOut;
  450. HANDLE EventArray[ 2 ];
  451. //
  452. // setup the event array
  453. //
  454. enum {
  455. SAC_CMD_LAUNCH_SUCCESS = 0,
  456. SAC_CMD_LAUNCH_FAILURE
  457. };
  458. ASSERT_STATUS(RequestSacCmdEventObjectBody != NULL, STATUS_INVALID_HANDLE);
  459. ASSERT_STATUS(RequestSacCmdSuccessEventWaitObjectBody != NULL, STATUS_INVALID_HANDLE);
  460. ASSERT_STATUS(RequestSacCmdFailureEventWaitObjectBody != NULL, STATUS_INVALID_HANDLE);
  461. #if ENABLE_CMD_SESSION_PERMISSION_CHECKING
  462. //
  463. // If we are not able to launch cmd sessions,
  464. // then return status unsuccessful.
  465. //
  466. if (! IsCommandConsoleLaunchingEnabled()) {
  467. return STATUS_UNSUCCESSFUL;
  468. }
  469. #endif
  470. //
  471. // Since we don't know if the user-mode app will serice our request properly
  472. // we have to timeout on the Serviced event.
  473. //
  474. TimeOut.QuadPart = Int32x32To64((LONG)90000, -1000);
  475. //
  476. // Populate the event array with events we want to catch from user-mode
  477. //
  478. EventArray[ 0 ] = RequestSacCmdSuccessEventWaitObjectBody;
  479. EventArray[ 1 ] = RequestSacCmdFailureEventWaitObjectBody;
  480. //
  481. // Set the event indicating that the communication buffer is
  482. // ready for the user-mode process. Because this is a synchronization
  483. // event, it automatically resets after releasing the waiting
  484. // user-mode thread. Note that we specify WaitNext to prevent the
  485. // race condition between setting this synchronization event and
  486. // waiting on the next one.
  487. //
  488. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  489. KdPrint(("SAC InvokeUserModeService: Sending Notification Event\n")));
  490. KeSetEvent(RequestSacCmdEventObjectBody,EVENT_INCREMENT,TRUE);
  491. //
  492. // Wait for the user-mode process to indicate that it is done
  493. // processing the request. We wait in user mode so that we can be
  494. // interrupted if necessary -- say, by an exit APC.
  495. //
  496. IF_SAC_DEBUG(
  497. SAC_DEBUG_FUNC_TRACE,
  498. KdPrint(("SAC InvokeUserModeService: Waiting for Serviced Event.\n"))
  499. );
  500. Status = KeWaitForMultipleObjects (
  501. sizeof(EventArray)/sizeof(HANDLE),
  502. EventArray,
  503. WaitAny,
  504. Executive,
  505. UserMode,
  506. FALSE,
  507. &TimeOut,
  508. NULL
  509. );
  510. switch (Status)
  511. {
  512. case SAC_CMD_LAUNCH_SUCCESS:
  513. Status = STATUS_SUCCESS;
  514. break;
  515. case SAC_CMD_LAUNCH_FAILURE:
  516. Status = STATUS_UNSUCCESSFUL;
  517. break;
  518. case STATUS_TIMEOUT:
  519. IF_SAC_DEBUG(
  520. SAC_DEBUG_FUNC_TRACE,
  521. KdPrint(("SAC InvokeUserModeService: KeWaitForMultipleObject timed-out %lx\n",Status))
  522. );
  523. //
  524. // We don't want to "reset" the cmd console event info
  525. // if the service times out for the following reason:
  526. //
  527. // The service may still be functional, but just unable
  528. // to respond because of machine load. We don't want
  529. // to remove it's registration and have it think it's
  530. // still registered, making the service useless.
  531. //
  532. NOTHING;
  533. break;
  534. default:
  535. IF_SAC_DEBUG(
  536. SAC_DEBUG_FAILS,
  537. KdPrint(("SAC InvokeUserModeService: KeWaitForMultipleObject returns %lx\n",Status))
  538. );
  539. Status = STATUS_UNSUCCESSFUL;
  540. break;
  541. }
  542. //
  543. // Return the status
  544. //
  545. return(Status);
  546. }
  547. VOID
  548. SacFormatMessage(
  549. PWSTR OutputString,
  550. PWSTR InputString,
  551. ULONG InputStringLength
  552. )
  553. /*++
  554. Routine Description:
  555. This routine parses the InputString for any control characters in the
  556. message, then converts those control characters.
  557. Arguments:
  558. OutputString - holds formatted string.
  559. InputString - original unformatted string.
  560. InputStringLength - length of unformatted string.
  561. Return Value:
  562. NONE
  563. --*/
  564. {
  565. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  566. KdPrint(("SAC SacFormatMessage: Entering.\n")));
  567. if( (InputString == NULL) ||
  568. (OutputString == NULL) ||
  569. (InputStringLength == 0) ) {
  570. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  571. KdPrint(("SAC SacFormatMessage: Exiting with invalid parameters.\n")));
  572. return;
  573. }
  574. while( (*InputString != L'\0') &&
  575. (InputStringLength) ) {
  576. if( *InputString == L'%' ) {
  577. //
  578. // Possibly a control sequence.
  579. //
  580. if( *(InputString+1) == L'0' ) {
  581. *OutputString = L'\0';
  582. OutputString++;
  583. goto SacFormatMessage_Done;
  584. } else if( *(InputString+1) == L'%' ) {
  585. *OutputString = L'%';
  586. OutputString++;
  587. InputString += 2;
  588. } else if( *(InputString+1) == L'\\' ) {
  589. *OutputString = L'\r';
  590. OutputString++;
  591. *OutputString = L'\n';
  592. OutputString++;
  593. InputString += 2;
  594. } else if( *(InputString+1) == L'r' ) {
  595. *OutputString = L'\r';
  596. InputString += 2;
  597. OutputString++;
  598. } else if( *(InputString+1) == L'b' ) {
  599. *OutputString = L' ';
  600. InputString += 2;
  601. OutputString++;
  602. } else if( *(InputString+1) == L'.' ) {
  603. *OutputString = L'.';
  604. InputString += 2;
  605. OutputString++;
  606. } else if( *(InputString+1) == L'!' ) {
  607. *OutputString = L'!';
  608. InputString += 2;
  609. OutputString++;
  610. } else {
  611. //
  612. // Don't know what this is. eat the '%' character.
  613. //
  614. InputString += 1;
  615. }
  616. } else {
  617. *OutputString++ = *InputString++;
  618. }
  619. InputStringLength--;
  620. }
  621. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  622. KdPrint(("SAC SacFormatMessage: Exiting.\n")));
  623. SacFormatMessage_Done:
  624. return;
  625. }
  626. NTSTATUS
  627. PreloadGlobalMessageTable(
  628. PVOID ImageBase
  629. )
  630. /*++
  631. Routine Description:
  632. This routine loads all of our message table entries into a global
  633. structure and
  634. Arguments:
  635. ImageBase - pointer to image base for locating resources
  636. Return Value:
  637. NTSTATUS code indicating outcome.
  638. --*/
  639. {
  640. ULONG Count,EntryCount;
  641. SIZE_T TotalSizeInBytes = 0;
  642. NTSTATUS Status;
  643. PMESSAGE_RESOURCE_ENTRY messageEntry;
  644. PWSTR pStringBuffer;
  645. PAGED_CODE( );
  646. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC PreloadGlobalMessageTable: Entering.\n")));
  647. //
  648. // if it already exists, then just return success
  649. //
  650. if (GlobalMessageTable != NULL) {
  651. Status = STATUS_SUCCESS;
  652. goto exit;
  653. }
  654. ASSERT( MESSAGE_FINAL > MESSAGE_INITIAL );
  655. //
  656. // get the total required size for the table.
  657. //
  658. for (Count = MESSAGE_INITIAL; Count != MESSAGE_FINAL ; Count++) {
  659. Status = RtlFindMessage(ImageBase,
  660. 11, // RT_MESSAGETABLE
  661. LANG_NEUTRAL,
  662. Count,
  663. &messageEntry
  664. );
  665. if (NT_SUCCESS(Status)) {
  666. //
  667. // add it on to our total size.
  668. //
  669. // the messageEntry size contains the structure size + the size of the text.
  670. //
  671. ASSERT(messageEntry->Flags & MESSAGE_RESOURCE_UNICODE);
  672. TotalSizeInBytes += sizeof(MESSAGE_TABLE_ENTRY) +
  673. (messageEntry->Length - FIELD_OFFSET(MESSAGE_RESOURCE_ENTRY, Text));
  674. GlobalMessageTableCount +=1;
  675. }
  676. }
  677. if (TotalSizeInBytes == 0) {
  678. IF_SAC_DEBUG(
  679. SAC_DEBUG_FAILS,
  680. KdPrint(("SAC PreloadGlobalMessageTable: No Messages.\n")));
  681. Status = STATUS_INVALID_PARAMETER;
  682. goto exit;
  683. }
  684. //
  685. // Allocate space for the table.
  686. //
  687. GlobalMessageTable = (PMESSAGE_TABLE_ENTRY) ALLOCATE_POOL( TotalSizeInBytes, GENERAL_POOL_TAG);
  688. if (!GlobalMessageTable) {
  689. Status = STATUS_NO_MEMORY;
  690. goto exit;
  691. }
  692. //
  693. // go through again, this time filling out the table with actual data
  694. //
  695. pStringBuffer = (PWSTR)((ULONG_PTR)GlobalMessageTable +
  696. (ULONG_PTR)(sizeof(MESSAGE_TABLE_ENTRY)*GlobalMessageTableCount));
  697. EntryCount = 0;
  698. for (Count = MESSAGE_INITIAL ; Count != MESSAGE_FINAL ; Count++) {
  699. Status = RtlFindMessage(ImageBase,
  700. 11, // RT_MESSAGETABLE
  701. LANG_NEUTRAL,
  702. Count,
  703. &messageEntry
  704. );
  705. if (NT_SUCCESS(Status)) {
  706. ULONG TextSize = messageEntry->Length - FIELD_OFFSET(MESSAGE_RESOURCE_ENTRY, Text);
  707. GlobalMessageTable[EntryCount].MessageId = Count;
  708. GlobalMessageTable[EntryCount].MessageText = pStringBuffer;
  709. //
  710. // Send the message through our Formatting filter as it passes
  711. // into our global message structure.
  712. //
  713. SacFormatMessage( pStringBuffer, (PWSTR)messageEntry->Text, TextSize );
  714. ASSERT( (ULONG)(wcslen(pStringBuffer)*sizeof(WCHAR)) <= TextSize );
  715. pStringBuffer = (PWSTR)((ULONG_PTR)pStringBuffer + (ULONG_PTR)(TextSize));
  716. EntryCount += 1;
  717. }
  718. }
  719. Status = STATUS_SUCCESS;
  720. exit:
  721. IF_SAC_DEBUG(
  722. SAC_DEBUG_FUNC_TRACE,
  723. KdPrint(("SAC PreloadGlobalMessageTable: Exiting with status 0x%0x.\n",
  724. Status)));
  725. return(Status);
  726. }
  727. NTSTATUS
  728. TearDownGlobalMessageTable(
  729. VOID
  730. )
  731. {
  732. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC PreloadGlobalMessageTable: Entering.\n")));
  733. SAFE_FREE_POOL( &GlobalMessageTable );
  734. IF_SAC_DEBUG(
  735. SAC_DEBUG_FUNC_TRACE,
  736. KdPrint(("SAC TearDownGlobalMessageTable: Exiting\n")));
  737. return(STATUS_SUCCESS);
  738. }
  739. PCWSTR
  740. GetMessage(
  741. ULONG MessageId
  742. )
  743. {
  744. PMESSAGE_TABLE_ENTRY pMessageTable;
  745. ULONG Count;
  746. if (!GlobalMessageTable) {
  747. return(NULL);
  748. }
  749. for (Count = 0; Count < GlobalMessageTableCount; Count++) {
  750. pMessageTable = &GlobalMessageTable[Count];
  751. if (pMessageTable->MessageId == MessageId) {
  752. return(pMessageTable->MessageText);
  753. }
  754. }
  755. ASSERT( FALSE );
  756. return(NULL);
  757. }
  758. NTSTATUS
  759. UTF8EncodeAndSend(
  760. PCWSTR OutputBuffer
  761. )
  762. /*++
  763. Routine Description:
  764. This is a convenience routine to simplify
  765. UFT8 encoding and sending a Unicode string.
  766. Arguments:
  767. OutputBuffer - the string to send
  768. Return Value:
  769. Status
  770. --*/
  771. {
  772. NTSTATUS Status;
  773. BOOLEAN bStatus;
  774. ULONG i;
  775. ULONG TranslatedCount;
  776. ULONG UTF8TranslationSize;
  777. Status = STATUS_SUCCESS;
  778. do {
  779. //
  780. // Display the output buffer
  781. //
  782. bStatus = SacTranslateUnicodeToUtf8(
  783. OutputBuffer,
  784. (ULONG)wcslen(OutputBuffer),
  785. (PUCHAR)Utf8ConversionBuffer,
  786. Utf8ConversionBufferSize,
  787. &UTF8TranslationSize,
  788. &TranslatedCount
  789. );
  790. if (! bStatus) {
  791. Status = STATUS_UNSUCCESSFUL;
  792. break;
  793. }
  794. //
  795. // Iterate through the uft8 buffer since
  796. // we can't be sure headless dispatch can
  797. // handle our entire string.
  798. //
  799. for (i = 0; i < UTF8TranslationSize; i ++) {
  800. Status = HeadlessDispatch(
  801. HeadlessCmdPutData,
  802. (PUCHAR)&(Utf8ConversionBuffer[i]),
  803. sizeof(UCHAR),
  804. NULL,
  805. NULL
  806. );
  807. if (! NT_SUCCESS(Status)) {
  808. break;
  809. }
  810. }
  811. } while ( FALSE );
  812. return Status;
  813. }
  814. BOOLEAN
  815. SacTranslateUtf8ToUnicode(
  816. UCHAR IncomingByte,
  817. UCHAR *ExistingUtf8Buffer,
  818. WCHAR *DestinationUnicodeVal
  819. )
  820. /*++
  821. Routine Description:
  822. Takes IncomingByte and concatenates it onto ExistingUtf8Buffer.
  823. Then attempts to decode the new contents of ExistingUtf8Buffer.
  824. Arguments:
  825. IncomingByte - New character to be appended onto
  826. ExistingUtf8Buffer.
  827. ExistingUtf8Buffer - running buffer containing incomplete UTF8
  828. encoded unicode value. When it gets full,
  829. we'll decode the value and return the
  830. corresponding Unicode value.
  831. Note that if we *do* detect a completed UTF8
  832. buffer and actually do a decode and return a
  833. Unicode value, then we will zero-fill the
  834. contents of ExistingUtf8Buffer.
  835. DestinationUnicodeVal - receives Unicode version of the UTF8 buffer.
  836. Note that if we do *not* detect a completed
  837. UTF8 buffer and thus can not return any data
  838. in DestinationUnicodeValue, then we will
  839. zero-fill the contents of DestinationUnicodeVal.
  840. Return Value:
  841. TRUE - We received a terminating character for our UTF8 buffer and will
  842. return a decoded Unicode value in DestinationUnicode.
  843. FALSE - We haven't yet received a terminating character for our UTF8
  844. buffer.
  845. --*/
  846. {
  847. // ULONG Count = 0;
  848. ULONG i = 0;
  849. BOOLEAN ReturnValue = FALSE;
  850. //
  851. // Insert our byte into ExistingUtf8Buffer.
  852. //
  853. i = 0;
  854. do {
  855. if( ExistingUtf8Buffer[i] == 0 ) {
  856. ExistingUtf8Buffer[i] = IncomingByte;
  857. break;
  858. }
  859. i++;
  860. } while( i < 3 );
  861. //
  862. // If we didn't get to actually insert our IncomingByte,
  863. // then someone sent us a fully-qualified UTF8 buffer.
  864. // This means we're about to drop IncomingByte.
  865. //
  866. // Drop the zero-th byte, shift everything over by one
  867. // and insert our new character.
  868. //
  869. // This implies that we should *never* need to zero out
  870. // the contents of ExistingUtf8Buffer unless we detect
  871. // a completed UTF8 packet. Otherwise, assume one of
  872. // these cases:
  873. // 1. We started listening mid-stream, so we caught the
  874. // last half of a UTF8 packet. In this case, we'll
  875. // end up shifting the contents of ExistingUtf8Buffer
  876. // until we detect a proper UTF8 start byte in the zero-th
  877. // position.
  878. // 2. We got some garbage character, which would invalidate
  879. // a UTF8 packet. By using the logic below, we would
  880. // end up disregarding that packet and waiting for
  881. // the next UTF8 packet to come in.
  882. if( i >= 3 ) {
  883. ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
  884. ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
  885. ExistingUtf8Buffer[2] = IncomingByte;
  886. }
  887. //
  888. // Attempt to convert the UTF8 buffer
  889. //
  890. // UTF8 decodes to Unicode in the following fashion:
  891. // If the high-order bit is 0 in the first byte:
  892. // 0xxxxxxx yyyyyyyy zzzzzzzz decodes to a Unicode value of 00000000 0xxxxxxx
  893. //
  894. // If the high-order 3 bits in the first byte == 6:
  895. // 110xxxxx 10yyyyyy zzzzzzzz decodes to a Unicode value of 00000xxx xxyyyyyy
  896. //
  897. // If the high-order 3 bits in the first byte == 7:
  898. // 1110xxxx 10yyyyyy 10zzzzzz decodes to a Unicode value of xxxxyyyy yyzzzzzz
  899. //
  900. IF_SAC_DEBUG(
  901. SAC_DEBUG_FUNC_TRACE,
  902. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - About to decode the UTF8 buffer.\n" ))
  903. );
  904. IF_SAC_DEBUG(
  905. SAC_DEBUG_FUNC_TRACE,
  906. KdPrint((" UTF8[0]: 0x%02lx UTF8[1]: 0x%02lx UTF8[2]: 0x%02lx\n",
  907. ExistingUtf8Buffer[0],
  908. ExistingUtf8Buffer[1],
  909. ExistingUtf8Buffer[2] ))
  910. );
  911. if( (ExistingUtf8Buffer[0] & 0x80) == 0 ) {
  912. IF_SAC_DEBUG(
  913. SAC_DEBUG_FUNC_TRACE,
  914. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - Case1\n" ))
  915. );
  916. //
  917. // First case described above. Just return the first byte
  918. // of our UTF8 buffer.
  919. //
  920. *DestinationUnicodeVal = (WCHAR)(ExistingUtf8Buffer[0]);
  921. //
  922. // We used 1 byte. Discard that byte and shift everything
  923. // in our buffer over by 1.
  924. //
  925. ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
  926. ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
  927. ExistingUtf8Buffer[2] = 0;
  928. ReturnValue = TRUE;
  929. } else if( (ExistingUtf8Buffer[0] & 0xE0) == 0xC0 ) {
  930. IF_SAC_DEBUG(
  931. SAC_DEBUG_FUNC_TRACE,
  932. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - 1st byte of UTF8 buffer says Case2\n"))
  933. );
  934. //
  935. // Second case described above. Decode the first 2 bytes of
  936. // of our UTF8 buffer.
  937. //
  938. if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
  939. IF_SAC_DEBUG(
  940. SAC_DEBUG_FUNC_TRACE,
  941. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - 2nd byte of UTF8 buffer says Case2.\n"))
  942. );
  943. // upper byte: 00000xxx
  944. *DestinationUnicodeVal = ((ExistingUtf8Buffer[0] >> 2) & 0x07);
  945. *DestinationUnicodeVal = *DestinationUnicodeVal << 8;
  946. // high bits of lower byte: xx000000
  947. *DestinationUnicodeVal |= ((ExistingUtf8Buffer[0] & 0x03) << 6);
  948. // low bits of lower byte: 00yyyyyy
  949. *DestinationUnicodeVal |= (ExistingUtf8Buffer[1] & 0x3F);
  950. //
  951. // We used 2 bytes. Discard those bytes and shift everything
  952. // in our buffer over by 2.
  953. //
  954. ExistingUtf8Buffer[0] = ExistingUtf8Buffer[2];
  955. ExistingUtf8Buffer[1] = 0;
  956. ExistingUtf8Buffer[2] = 0;
  957. ReturnValue = TRUE;
  958. }
  959. } else if( (ExistingUtf8Buffer[0] & 0xF0) == 0xE0 ) {
  960. IF_SAC_DEBUG(
  961. SAC_DEBUG_FUNC_TRACE,
  962. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - 1st byte of UTF8 buffer says Case3\n" ))
  963. );
  964. //
  965. // Third case described above. Decode the all 3 bytes of
  966. // of our UTF8 buffer.
  967. //
  968. if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
  969. IF_SAC_DEBUG(
  970. SAC_DEBUG_FUNC_TRACE,
  971. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - 2nd byte of UTF8 buffer says Case3\n" ))
  972. );
  973. if( (ExistingUtf8Buffer[2] & 0xC0) == 0x80 ) {
  974. IF_SAC_DEBUG(
  975. SAC_DEBUG_FUNC_TRACE,
  976. KdPrint(("SACDRV: SacTranslateUtf8ToUnicode - 3rd byte of UTF8 buffer says Case3\n" ))
  977. );
  978. // upper byte: xxxx0000
  979. *DestinationUnicodeVal = ((ExistingUtf8Buffer[0] << 4) & 0xF0);
  980. // upper byte: 0000yyyy
  981. *DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] >> 2) & 0x0F);
  982. *DestinationUnicodeVal = *DestinationUnicodeVal << 8;
  983. // lower byte: yy000000
  984. *DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] << 6) & 0xC0);
  985. // lower byte: 00zzzzzz
  986. *DestinationUnicodeVal |= (ExistingUtf8Buffer[2] & 0x3F);
  987. //
  988. // We used all 3 bytes. Zero out the buffer.
  989. //
  990. ExistingUtf8Buffer[0] = 0;
  991. ExistingUtf8Buffer[1] = 0;
  992. ExistingUtf8Buffer[2] = 0;
  993. ReturnValue = TRUE;
  994. }
  995. }
  996. }
  997. return ReturnValue;
  998. }
  999. BOOLEAN
  1000. SacTranslateUnicodeToUtf8(
  1001. IN PCWSTR SourceBuffer,
  1002. IN ULONG SourceBufferLength,
  1003. IN PUCHAR DestinationBuffer,
  1004. IN ULONG DestinationBufferSize,
  1005. OUT PULONG UTF8Count,
  1006. OUT PULONG ProcessedCount
  1007. )
  1008. /*++
  1009. Routine Description:
  1010. This routine translates a Unicode string into a UFT8
  1011. encoded string.
  1012. Note: if the destination buffer is not large enough to hold
  1013. the entire encoded UFT8 string, then it will contain
  1014. as much as can fit.
  1015. TODO: this routine should return some notification if
  1016. the entire Unicode string was not encoded.
  1017. Arguments:
  1018. SourceBuffer - the source Unicode string
  1019. SourceBufferLength - the # of characters the caller wants to translate
  1020. note: a NULL termination overrides this
  1021. DestinationBuffer - the destination for the UTF8 string
  1022. DestinationBufferSize - the size of the destination buffer
  1023. UTF8Count - on exit, contains the # of resulting UTF8 characters
  1024. ProcessedCount - on exit, contains the # of processed Unicode characters
  1025. Return Value:
  1026. Status
  1027. --*/
  1028. {
  1029. //
  1030. // Init
  1031. //
  1032. *UTF8Count = 0;
  1033. *ProcessedCount = 0;
  1034. //
  1035. // convert into UTF8 for actual transmission
  1036. //
  1037. // UTF-8 encodes 2-byte Unicode characters as follows:
  1038. // If the first nine bits are zero (00000000 0xxxxxxx), encode it as one byte 0xxxxxxx
  1039. // If the first five bits are zero (00000yyy yyxxxxxx), encode it as two bytes 110yyyyy 10xxxxxx
  1040. // Otherwise (zzzzyyyy yyxxxxxx), encode it as three bytes 1110zzzz 10yyyyyy 10xxxxxx
  1041. //
  1042. //
  1043. // Process until one of the specified conditions is met
  1044. //
  1045. while (*SourceBuffer &&
  1046. (*UTF8Count < DestinationBufferSize) &&
  1047. (*ProcessedCount < SourceBufferLength)
  1048. ) {
  1049. if( (*SourceBuffer & 0xFF80) == 0 ) {
  1050. //
  1051. // if the top 9 bits are zero, then just
  1052. // encode as 1 byte. (ASCII passes through unchanged).
  1053. //
  1054. DestinationBuffer[(*UTF8Count)++] = (UCHAR)(*SourceBuffer & 0x7F);
  1055. } else if( (*SourceBuffer & 0xF800) == 0 ) {
  1056. //
  1057. // see if we pass the end of the buffer
  1058. //
  1059. if ((*UTF8Count + 2) >= DestinationBufferSize) {
  1060. break;
  1061. }
  1062. //
  1063. // if the top 5 bits are zero, then encode as 2 bytes
  1064. //
  1065. DestinationBuffer[(*UTF8Count)++] = (UCHAR)((*SourceBuffer >> 6) & 0x1F) | 0xC0;
  1066. DestinationBuffer[(*UTF8Count)++] = (UCHAR)(*SourceBuffer & 0xBF) | 0x80;
  1067. } else {
  1068. //
  1069. // see if we pass the end of the buffer
  1070. //
  1071. if ((*UTF8Count + 3) >= DestinationBufferSize) {
  1072. break;
  1073. }
  1074. //
  1075. // encode as 3 bytes
  1076. //
  1077. DestinationBuffer[(*UTF8Count)++] = (UCHAR)((*SourceBuffer >> 12) & 0xF) | 0xE0;
  1078. DestinationBuffer[(*UTF8Count)++] = (UCHAR)((*SourceBuffer >> 6) & 0x3F) | 0x80;
  1079. DestinationBuffer[(*UTF8Count)++] = (UCHAR)(*SourceBuffer & 0xBF) | 0x80;
  1080. }
  1081. //
  1082. // Advance the # of characters processed
  1083. //
  1084. (*ProcessedCount)++;
  1085. //
  1086. // Advanced to the next character to process
  1087. //
  1088. SourceBuffer += 1;
  1089. }
  1090. //
  1091. // Sanity checks
  1092. //
  1093. ASSERT(*ProcessedCount <= SourceBufferLength);
  1094. ASSERT(*UTF8Count <= DestinationBufferSize);
  1095. return(TRUE);
  1096. }
  1097. VOID
  1098. AppendMessage(
  1099. PWSTR OutPutBuffer,
  1100. ULONG MessageId,
  1101. PWSTR ValueBuffer OPTIONAL
  1102. )
  1103. /*++
  1104. Routine Description:
  1105. This function will insert the valuestring into the specified message, then
  1106. concatenate the resulting string onto the OutPutBuffer.
  1107. Arguments:
  1108. OutPutBuffer The resulting String.
  1109. MessageId ID of the formatting message to use.
  1110. ValueBUffer Value string to be inserted into the message.
  1111. Return Value:
  1112. NONE
  1113. --*/
  1114. {
  1115. PWSTR MyTemporaryBuffer = NULL;
  1116. PCWSTR p;
  1117. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  1118. KdPrint(("SAC AppendMessage: Entering.\n")));
  1119. p = GetMessage(MessageId);
  1120. if( p == NULL ) {
  1121. return;
  1122. }
  1123. if( ValueBuffer == NULL ) {
  1124. wcscat( OutPutBuffer, p );
  1125. } else {
  1126. MyTemporaryBuffer = (PWSTR)(wcschr(OutPutBuffer, L'\0'));
  1127. if( MyTemporaryBuffer == NULL ) {
  1128. MyTemporaryBuffer = OutPutBuffer;
  1129. }
  1130. swprintf( MyTemporaryBuffer, p, ValueBuffer );
  1131. }
  1132. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  1133. KdPrint(("SAC AppendMessage: Entering.\n")));
  1134. return;
  1135. }
  1136. NTSTATUS
  1137. GetRegistryValueBuffer(
  1138. PWSTR KeyName,
  1139. PWSTR ValueName,
  1140. PKEY_VALUE_PARTIAL_INFORMATION* ValueBuffer
  1141. )
  1142. /*++
  1143. Routine Description:
  1144. This function will go query the registry and pull the specified Value.
  1145. Arguments:
  1146. KeyName Name of the registry key we'll be querying.
  1147. ValueName Name of the registry value we'll be querying.
  1148. ValueBuffer On success, contains value
  1149. Return Value:
  1150. NTSTATUS.
  1151. --*/
  1152. {
  1153. NTSTATUS Status = STATUS_SUCCESS;
  1154. ULONG KeyValueLength;
  1155. OBJECT_ATTRIBUTES Obja;
  1156. UNICODE_STRING UnicodeString;
  1157. HANDLE KeyHandle;
  1158. IF_SAC_DEBUG(
  1159. SAC_DEBUG_FUNC_TRACE,
  1160. KdPrint(("SAC GetRegistryValueBuffer: Entering.\n"))
  1161. );
  1162. ASSERT_STATUS(KeyName, STATUS_INVALID_PARAMETER_1);
  1163. ASSERT_STATUS(ValueName, STATUS_INVALID_PARAMETER_2);
  1164. do {
  1165. //
  1166. // Get the reg key handle
  1167. //
  1168. INIT_OBJA( &Obja, &UnicodeString, KeyName );
  1169. Status = ZwOpenKey(
  1170. &KeyHandle,
  1171. KEY_READ,
  1172. &Obja
  1173. );
  1174. if( !NT_SUCCESS(Status) ) {
  1175. IF_SAC_DEBUG(
  1176. SAC_DEBUG_FUNC_TRACE,
  1177. KdPrint(("SAC GetRegistryValueBuffer: failed ZwOpenKey: %X\n", Status))
  1178. );
  1179. return Status;
  1180. }
  1181. //
  1182. // Get the value buffer size
  1183. //
  1184. RtlInitUnicodeString( &UnicodeString, ValueName );
  1185. KeyValueLength = 0;
  1186. Status = ZwQueryValueKey(
  1187. KeyHandle,
  1188. &UnicodeString,
  1189. KeyValuePartialInformation,
  1190. (PVOID)NULL,
  1191. 0,
  1192. &KeyValueLength
  1193. );
  1194. if( KeyValueLength == 0 ) {
  1195. IF_SAC_DEBUG(
  1196. SAC_DEBUG_FUNC_TRACE,
  1197. KdPrint(("SAC GetRegistryValueBuffer: failed ZwQueryValueKey: %X\n", Status))
  1198. );
  1199. break;
  1200. }
  1201. //
  1202. // Allocate the value buffer
  1203. //
  1204. KeyValueLength += 4;
  1205. *ValueBuffer = (PKEY_VALUE_PARTIAL_INFORMATION)ALLOCATE_POOL( KeyValueLength, GENERAL_POOL_TAG );
  1206. if( *ValueBuffer == NULL ) {
  1207. IF_SAC_DEBUG(
  1208. SAC_DEBUG_FUNC_TRACE,
  1209. KdPrint(("SAC GetRegistryValueBuffer: failed allocation\n"))
  1210. );
  1211. break;
  1212. }
  1213. //
  1214. // Get the value
  1215. //
  1216. Status = ZwQueryValueKey(
  1217. KeyHandle,
  1218. &UnicodeString,
  1219. KeyValuePartialInformation,
  1220. *ValueBuffer,
  1221. KeyValueLength,
  1222. &KeyValueLength
  1223. );
  1224. if( !NT_SUCCESS(Status) ) {
  1225. IF_SAC_DEBUG(
  1226. SAC_DEBUG_FUNC_TRACE,
  1227. KdPrint(("SAC GetRegistryValueBuffer: failed ZwQueryValueKey: %X\n", Status))
  1228. );
  1229. FREE_POOL( ValueBuffer );
  1230. break;
  1231. }
  1232. } while ( FALSE );
  1233. //
  1234. // We are done with the reg key
  1235. //
  1236. NtClose(KeyHandle);
  1237. IF_SAC_DEBUG(
  1238. SAC_DEBUG_FUNC_TRACE,
  1239. KdPrint(("SAC GetRegistryValueBuffer: Exiting.\n"))
  1240. );
  1241. return Status;
  1242. }
  1243. NTSTATUS
  1244. SetRegistryValue(
  1245. IN PWSTR KeyName,
  1246. IN PWSTR ValueName,
  1247. IN ULONG Type,
  1248. IN PVOID Data,
  1249. IN ULONG DataSize
  1250. )
  1251. /*++
  1252. Routine Description:
  1253. This function will set the specified registry key value.
  1254. Arguments:
  1255. KeyName Name of the registry key we'll be querying.
  1256. ValueName Name of the registry value we'll be querying.
  1257. Type Registry value type
  1258. Data New value data
  1259. DataSize Size of the new value data
  1260. Return Value:
  1261. NTSTATUS.
  1262. --*/
  1263. {
  1264. NTSTATUS Status;
  1265. OBJECT_ATTRIBUTES Obja;
  1266. UNICODE_STRING UnicodeString;
  1267. HANDLE KeyHandle;
  1268. IF_SAC_DEBUG(
  1269. SAC_DEBUG_FUNC_TRACE,
  1270. KdPrint(("SAC SetRegistryValue: Entering.\n"))
  1271. );
  1272. ASSERT_STATUS(KeyName, STATUS_INVALID_PARAMETER_1);
  1273. ASSERT_STATUS(ValueName, STATUS_INVALID_PARAMETER_2);
  1274. ASSERT_STATUS(Data, STATUS_INVALID_PARAMETER_4);
  1275. do {
  1276. //
  1277. // Get the reg key handle
  1278. //
  1279. INIT_OBJA( &Obja, &UnicodeString, KeyName );
  1280. Status = ZwOpenKey(
  1281. &KeyHandle,
  1282. KEY_WRITE,
  1283. &Obja
  1284. );
  1285. if( !NT_SUCCESS(Status) ) {
  1286. IF_SAC_DEBUG(
  1287. SAC_DEBUG_FUNC_TRACE,
  1288. KdPrint(("SAC SetRegistryValue: failed ZwOpenKey: %X.\n", Status))
  1289. );
  1290. return Status;
  1291. }
  1292. //
  1293. // Set the value
  1294. //
  1295. RtlInitUnicodeString( &UnicodeString, ValueName );
  1296. Status = ZwSetValueKey(
  1297. KeyHandle,
  1298. &UnicodeString,
  1299. 0,
  1300. Type,
  1301. Data,
  1302. DataSize
  1303. );
  1304. if( !NT_SUCCESS(Status) ) {
  1305. IF_SAC_DEBUG(
  1306. SAC_DEBUG_FUNC_TRACE,
  1307. KdPrint(("SAC SetRegistryValue: failed ZwSetValueKey: %X\n", Status))
  1308. );
  1309. break;
  1310. }
  1311. } while ( FALSE );
  1312. //
  1313. // We are done with the reg key
  1314. //
  1315. NtClose(KeyHandle);
  1316. IF_SAC_DEBUG(
  1317. SAC_DEBUG_FUNC_TRACE,
  1318. KdPrint(("SAC SetRegistryValue: Exiting.\n"))
  1319. );
  1320. return Status;
  1321. }
  1322. NTSTATUS
  1323. CopyRegistryValueData(
  1324. PVOID* Dest,
  1325. PKEY_VALUE_PARTIAL_INFORMATION ValueBuffer
  1326. )
  1327. /*++
  1328. Routine Description:
  1329. This routine allocates and copies the specified registry value data
  1330. Arguments:
  1331. Dest - on success, contains the value data copy
  1332. ValueBuffer - contains the value data
  1333. Return Value:
  1334. Status
  1335. --*/
  1336. {
  1337. NTSTATUS Status;
  1338. Status = STATUS_SUCCESS;
  1339. ASSERT_STATUS(Dest, STATUS_INVALID_PARAMETER_1);
  1340. ASSERT_STATUS(ValueBuffer, STATUS_INVALID_PARAMETER_2);
  1341. do {
  1342. *Dest = (PVOID)ALLOCATE_POOL(ValueBuffer->DataLength, GENERAL_POOL_TAG);
  1343. if (*Dest == NULL) {
  1344. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC CopyRegistryValueBuffer: Failed ALLOCATE.\n")));
  1345. Status = STATUS_NO_MEMORY;
  1346. break;
  1347. }
  1348. RtlCopyMemory(*Dest, ValueBuffer->Data, ValueBuffer->DataLength);
  1349. } while (FALSE);
  1350. return Status;
  1351. }
  1352. NTSTATUS
  1353. TranslateMachineInformationText(
  1354. PWSTR* Buffer
  1355. )
  1356. /*++
  1357. Routine Description:
  1358. This routine creates a formated text string representing
  1359. the current machine info
  1360. Arguments:
  1361. Buffer - Contains the machine info string
  1362. Return Value:
  1363. Status
  1364. --*/
  1365. {
  1366. NTSTATUS Status;
  1367. PCWSTR pwStr;
  1368. PWSTR pBuffer;
  1369. ULONG len;
  1370. ULONG Size;
  1371. #define MITEXT_SPRINTF(_s,_t) \
  1372. pwStr = GetMessage(_s); \
  1373. if (pwStr && MachineInformation->_t) { \
  1374. len = swprintf( \
  1375. pBuffer, \
  1376. pwStr, \
  1377. MachineInformation->_t \
  1378. ); \
  1379. pBuffer += len; \
  1380. }
  1381. #define MITEXT_LENGTH(_s,_t) \
  1382. pwStr = GetMessage(_s); \
  1383. if (pwStr && MachineInformation->_t) { \
  1384. len += (ULONG)wcslen(MachineInformation->_t) + (ULONG)wcslen(pwStr); \
  1385. }
  1386. ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_1);
  1387. //
  1388. // default: we succeeded
  1389. //
  1390. Status = STATUS_SUCCESS;
  1391. //
  1392. // Assemble the machine info
  1393. //
  1394. do {
  1395. //
  1396. // compute the length of the final string so
  1397. // we know how much memory to allocate
  1398. //
  1399. {
  1400. len = 0;
  1401. MITEXT_LENGTH(SAC_MACHINEINFO_COMPUTERNAME, MachineName);
  1402. MITEXT_LENGTH(SAC_MACHINEINFO_GUID, GUID);
  1403. MITEXT_LENGTH(SAC_MACHINEINFO_PROCESSOR_ARCHITECTURE, ProcessorArchitecture);
  1404. MITEXT_LENGTH(SAC_MACHINEINFO_OS_VERSION, OSVersion);
  1405. MITEXT_LENGTH(SAC_MACHINEINFO_OS_BUILD, OSBuildNumber);
  1406. MITEXT_LENGTH(SAC_MACHINEINFO_OS_PRODUCTTYPE, OSProductType);
  1407. MITEXT_LENGTH(SAC_MACHINEINFO_SERVICE_PACK, OSServicePack);
  1408. //
  1409. // compute the size; include NULL termination
  1410. //
  1411. Size = (len + 1) * sizeof(WCHAR);
  1412. }
  1413. *Buffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1414. if( *Buffer == NULL ) {
  1415. Status = STATUS_NO_MEMORY;
  1416. break;
  1417. }
  1418. pBuffer = *Buffer;
  1419. MITEXT_SPRINTF(SAC_MACHINEINFO_COMPUTERNAME, MachineName);
  1420. MITEXT_SPRINTF(SAC_MACHINEINFO_GUID, GUID);
  1421. MITEXT_SPRINTF(SAC_MACHINEINFO_PROCESSOR_ARCHITECTURE, ProcessorArchitecture);
  1422. MITEXT_SPRINTF(SAC_MACHINEINFO_OS_VERSION, OSVersion);
  1423. MITEXT_SPRINTF(SAC_MACHINEINFO_OS_BUILD, OSBuildNumber);
  1424. MITEXT_SPRINTF(SAC_MACHINEINFO_OS_PRODUCTTYPE, OSProductType);
  1425. MITEXT_SPRINTF(SAC_MACHINEINFO_SERVICE_PACK, OSServicePack);
  1426. ASSERT((ULONG)((wcslen(*Buffer) + 1) * sizeof(WCHAR)) <= Size);
  1427. } while ( FALSE );
  1428. if (!NT_SUCCESS(Status) && *Buffer != NULL) {
  1429. FREE_POOL(Buffer);
  1430. *Buffer = NULL;
  1431. }
  1432. return Status;
  1433. }
  1434. NTSTATUS
  1435. TranslateMachineInformationXML(
  1436. OUT PWSTR* Buffer,
  1437. IN PWSTR AdditionalInfo
  1438. )
  1439. /*++
  1440. Routine Description:
  1441. This routine creates an XML string representing the current machine info
  1442. Arguments:
  1443. Buffer - Contains the machine info string
  1444. AdditionalInfo - Additional Machine Info wanted to be included by caller
  1445. Note: additional info should be a well-formed xml string:
  1446. e.g. <uptime>01:01:01</uptime>
  1447. Return Value:
  1448. Status
  1449. --*/
  1450. {
  1451. NTSTATUS Status;
  1452. PCWSTR pwStr;
  1453. PWSTR pBuffer;
  1454. ULONG len;
  1455. ULONG Size;
  1456. #define MIXML_SPRINTF(_s,_t) \
  1457. pwStr = _s; \
  1458. if (pwStr && MachineInformation->_t) { \
  1459. len = swprintf( \
  1460. pBuffer, \
  1461. pwStr, \
  1462. MachineInformation->_t \
  1463. ); \
  1464. pBuffer += len; \
  1465. }
  1466. #define XML_MACHINEINFO_HEADER L"<machine-info>\r\n"
  1467. #define XML_MACHINEINFO_NAME L"<name>%s</name>\r\n"
  1468. #define XML_MACHINEINFO_GUID L"<guid>%s</guid>\r\n"
  1469. #define XML_MACHINEINFO_PROCESSOR_ARCHITECTURE L"<processor-architecture>%s</processor-architecture>\r\n"
  1470. #define XML_MACHINEINFO_OS_VERSION L"<os-version>%s</os-version>\r\n"
  1471. #define XML_MACHINEINFO_OS_BUILD L"<os-build-number>%s</os-build-number>\r\n"
  1472. #define XML_MACHINEINFO_OS_PRODUCTTYPE L"<os-product>%s</os-product>\r\n"
  1473. #define XML_MACHINEINFO_SERVICE_PACK L"<os-service-pack>%s</os-service-pack>\r\n"
  1474. #define XML_MACHINEINFO_FOOTER L"</machine-info>\r\n"
  1475. ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_1);
  1476. //
  1477. // default: we succeeded
  1478. //
  1479. Status = STATUS_SUCCESS;
  1480. //
  1481. // Assemble the machine info
  1482. //
  1483. do {
  1484. //
  1485. // compute the length of the final string so
  1486. // we know how much memory to allocate
  1487. //
  1488. {
  1489. len = (ULONG)wcslen(XML_MACHINEINFO_HEADER);
  1490. if (MachineInformation->MachineName) {
  1491. len += (ULONG)wcslen(MachineInformation->MachineName);
  1492. len += (ULONG)wcslen(XML_MACHINEINFO_NAME);
  1493. }
  1494. if (MachineInformation->GUID) {
  1495. len += (ULONG)wcslen(MachineInformation->GUID);
  1496. len += (ULONG)wcslen(XML_MACHINEINFO_GUID);
  1497. }
  1498. if (MachineInformation->ProcessorArchitecture) {
  1499. len += (ULONG)wcslen(MachineInformation->ProcessorArchitecture);
  1500. len += (ULONG)wcslen(XML_MACHINEINFO_PROCESSOR_ARCHITECTURE);
  1501. }
  1502. if (MachineInformation->OSVersion) {
  1503. len += (ULONG)wcslen(MachineInformation->OSVersion);
  1504. len += (ULONG)wcslen(XML_MACHINEINFO_OS_VERSION);
  1505. }
  1506. if (MachineInformation->OSBuildNumber) {
  1507. len += (ULONG)wcslen(MachineInformation->OSBuildNumber);
  1508. len += (ULONG)wcslen(XML_MACHINEINFO_OS_BUILD);
  1509. }
  1510. if (MachineInformation->OSProductType) {
  1511. len += (ULONG)wcslen(MachineInformation->OSProductType);
  1512. len += (ULONG)wcslen(XML_MACHINEINFO_OS_PRODUCTTYPE);
  1513. }
  1514. if (MachineInformation->OSServicePack) {
  1515. len += (ULONG)wcslen(MachineInformation->OSServicePack);
  1516. len += (ULONG)wcslen(XML_MACHINEINFO_SERVICE_PACK);
  1517. }
  1518. //
  1519. // If the caller passed additional machine info,
  1520. // then account for the additional len
  1521. //
  1522. if (AdditionalInfo) {
  1523. len += (ULONG)wcslen(AdditionalInfo);
  1524. }
  1525. len += (ULONG)wcslen(XML_MACHINEINFO_FOOTER);
  1526. //
  1527. // compute the size; include NULL termination
  1528. //
  1529. Size = (len + 1) * sizeof(WCHAR);
  1530. }
  1531. //
  1532. // Allocate the machine info buffer
  1533. //
  1534. *Buffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1535. if( *Buffer == NULL ) {
  1536. Status = STATUS_NO_MEMORY;
  1537. break;
  1538. }
  1539. pBuffer = *Buffer;
  1540. len = (ULONG)wcslen(XML_MACHINEINFO_HEADER);
  1541. wcscpy(pBuffer, XML_MACHINEINFO_HEADER);
  1542. pBuffer += len;
  1543. MIXML_SPRINTF(XML_MACHINEINFO_NAME, MachineName);
  1544. MIXML_SPRINTF(XML_MACHINEINFO_GUID, GUID);
  1545. MIXML_SPRINTF(XML_MACHINEINFO_PROCESSOR_ARCHITECTURE, ProcessorArchitecture);
  1546. MIXML_SPRINTF(XML_MACHINEINFO_OS_VERSION, OSVersion);
  1547. MIXML_SPRINTF(XML_MACHINEINFO_OS_BUILD, OSBuildNumber);
  1548. MIXML_SPRINTF(XML_MACHINEINFO_OS_PRODUCTTYPE, OSProductType);
  1549. MIXML_SPRINTF(XML_MACHINEINFO_SERVICE_PACK, OSServicePack);
  1550. //
  1551. // If present, include the additional info
  1552. //
  1553. if (AdditionalInfo) {
  1554. len = (ULONG)wcslen(AdditionalInfo);
  1555. wcscpy(pBuffer, AdditionalInfo);
  1556. pBuffer += len;
  1557. }
  1558. wcscpy(pBuffer, XML_MACHINEINFO_FOOTER);
  1559. ASSERT((((ULONG)wcslen(*Buffer) + 1) * sizeof(WCHAR)) <= Size);
  1560. } while ( FALSE );
  1561. if (!NT_SUCCESS(Status) && *Buffer != NULL) {
  1562. FREE_POOL(Buffer);
  1563. *Buffer = NULL;
  1564. }
  1565. return Status;
  1566. }
  1567. NTSTATUS
  1568. RegisterBlueScreenMachineInformation(
  1569. VOID
  1570. )
  1571. /*++
  1572. Routine Description:
  1573. This routine populates Headless Dispatch Blue Screen handler
  1574. with the XML representation machine information
  1575. Arguments:
  1576. None.
  1577. Return Value:
  1578. Status
  1579. --*/
  1580. {
  1581. PHEADLESS_CMD_SET_BLUE_SCREEN_DATA BSBuffer;
  1582. PWSTR XMLBuffer;
  1583. ULONG XMLBufferLength;
  1584. NTSTATUS Status;
  1585. ULONG Size;
  1586. PSTR XML_TAG = "MACHINEINFO";
  1587. ULONG XML_TAG_LENGTH;
  1588. //
  1589. // Get the XML representation of the machine info
  1590. //
  1591. Status = TranslateMachineInformationXML(&XMLBuffer, NULL);
  1592. ASSERT_STATUS(NT_SUCCESS(Status), Status);
  1593. ASSERT_STATUS(XMLBuffer, STATUS_UNSUCCESSFUL);
  1594. //
  1595. // Determine the lengths of the strings we'll use
  1596. //
  1597. XMLBufferLength = (ULONG)wcslen(XMLBuffer);
  1598. XML_TAG_LENGTH = (ULONG)strlen(XML_TAG);
  1599. //
  1600. // Allocate the BS Buffer
  1601. //
  1602. // Need to accomodate:
  1603. //
  1604. // HEADLESS_CMD_SET_BLUE_SCREEN_DATA + XML_TAG\0XMLBuffer\0
  1605. //
  1606. Size = sizeof(HEADLESS_CMD_SET_BLUE_SCREEN_DATA) +
  1607. (XML_TAG_LENGTH*sizeof(UCHAR)) + sizeof(UCHAR) +
  1608. (XMLBufferLength*sizeof(UCHAR)) + sizeof(UCHAR);
  1609. BSBuffer = (PHEADLESS_CMD_SET_BLUE_SCREEN_DATA)ALLOCATE_POOL(
  1610. Size,
  1611. GENERAL_POOL_TAG
  1612. );
  1613. if (!BSBuffer) {
  1614. FREE_POOL(&XMLBuffer);
  1615. }
  1616. ASSERT_STATUS(BSBuffer, STATUS_NO_MEMORY);
  1617. //
  1618. // Copy the XML Buffer into the BS Buffer as an ANSI string
  1619. //
  1620. {
  1621. PUCHAR pch;
  1622. ULONG i;
  1623. //
  1624. // Get the BScreen buffer
  1625. //
  1626. pch = &(BSBuffer->Data[0]);
  1627. //
  1628. // Insert the XML Tag (required for HeadlessDispatch)
  1629. //
  1630. strcpy((char *)pch, XML_TAG);
  1631. //
  1632. // Move to the beginning of the XML buffer region
  1633. //
  1634. BSBuffer->ValueIndex = XML_TAG_LENGTH+1;
  1635. pch += XML_TAG_LENGTH+1;
  1636. //
  1637. // Write the WCHAR XMLBuffer as ANSI into the BSBuffer
  1638. //
  1639. for (i = 0; i < XMLBufferLength; i++) {
  1640. pch[i] = (UCHAR)XMLBuffer[i];
  1641. }
  1642. pch[i] = '\0';
  1643. }
  1644. //
  1645. // ========
  1646. // Insert it all into the BLUESCREEN data.
  1647. // ========
  1648. //
  1649. Status = HeadlessDispatch(
  1650. HeadlessCmdSetBlueScreenData,
  1651. BSBuffer,
  1652. Size,
  1653. NULL,
  1654. 0
  1655. );
  1656. //
  1657. // clean up
  1658. //
  1659. FREE_POOL( &BSBuffer );
  1660. FREE_POOL( &XMLBuffer);
  1661. IF_SAC_DEBUG(
  1662. SAC_DEBUG_FUNC_TRACE,
  1663. KdPrint(("SAC Initialize Machine Information: Exiting.\n"))
  1664. );
  1665. return Status;
  1666. }
  1667. VOID
  1668. FreeMachineInformation(
  1669. VOID
  1670. )
  1671. /*++
  1672. Routine Description:
  1673. This routine releases the machine information collected at driver startup
  1674. Arguments:
  1675. None
  1676. Return Value:
  1677. None
  1678. --*/
  1679. {
  1680. //
  1681. // The information should be present
  1682. //
  1683. ASSERT(MachineInformation);
  1684. if (!MachineInformation) {
  1685. return;
  1686. }
  1687. SAFE_FREE_POOL(&MachineInformation->MachineName);
  1688. SAFE_FREE_POOL(&MachineInformation->GUID);
  1689. SAFE_FREE_POOL(&MachineInformation->ProcessorArchitecture);
  1690. SAFE_FREE_POOL(&MachineInformation->OSVersion);
  1691. SAFE_FREE_POOL(&MachineInformation->OSBuildNumber);
  1692. SAFE_FREE_POOL(&MachineInformation->OSProductType);
  1693. SAFE_FREE_POOL(&MachineInformation->OSServicePack);
  1694. }
  1695. VOID
  1696. InitializeMachineInformation(
  1697. VOID
  1698. )
  1699. /*++
  1700. Routine Description:
  1701. This function initializes the global variable MachineInformationBuffer.
  1702. We'll gather a whole bunch of information about the machine and fill
  1703. in the buffer.
  1704. Arguments:
  1705. None.
  1706. Return Value:
  1707. None.
  1708. --*/
  1709. {
  1710. PWSTR COMPUTERNAME_KEY_NAME = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\ComputerName";
  1711. PWSTR COMPUTERNAME_VALUE_NAME = L"ComputerName";
  1712. PWSTR PROCESSOR_ARCHITECTURE_KEY_NAME = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Environment";
  1713. PWSTR PROCESSOR_ARCHITECTURE_VALUE_NAME = L"PROCESSOR_ARCHITECTURE";
  1714. PWSTR SETUP_KEY_NAME = L"\\Registry\\Machine\\System\\Setup";
  1715. PWSTR SETUPINPROGRESS_VALUE_NAME = L"SystemSetupInProgress";
  1716. RTL_OSVERSIONINFOEXW VersionInfo;
  1717. PKEY_VALUE_PARTIAL_INFORMATION ValueBuffer;
  1718. NTSTATUS Status = STATUS_SUCCESS;
  1719. SIZE_T i;
  1720. PWSTR MyTemporaryBufferW = NULL;
  1721. GUID MyGUID;
  1722. PCWSTR pwStr;
  1723. BOOLEAN InGuiModeSetup = FALSE;
  1724. IF_SAC_DEBUG(
  1725. SAC_DEBUG_FUNC_TRACE,
  1726. KdPrint(("SAC Initialize Machine Information: Entering.\n"))
  1727. );
  1728. if( MachineInformation != NULL ) {
  1729. //
  1730. // someone called us again!
  1731. //
  1732. IF_SAC_DEBUG(
  1733. SAC_DEBUG_FUNC_TRACE_LOUD,
  1734. KdPrint(("SAC Initialize Machine Information:: MachineInformationBuffer already initialzied.\n"))
  1735. );
  1736. return;
  1737. } else {
  1738. MachineInformation = (PMACHINE_INFORMATION)ALLOCATE_POOL( sizeof(MACHINE_INFORMATION), GENERAL_POOL_TAG );
  1739. if( MachineInformation == NULL ) {
  1740. goto InitializeMachineInformation_Failure;
  1741. }
  1742. }
  1743. RtlZeroMemory( MachineInformation, sizeof(MACHINE_INFORMATION) );
  1744. //
  1745. // We're real early in the boot process, so we're going to take for granted that the machine hasn't
  1746. // bugchecked. This means that we can safely call some kernel functions to go figure out what
  1747. // platform we're running on.
  1748. //
  1749. RtlZeroMemory( &VersionInfo, sizeof(VersionInfo));
  1750. Status = RtlGetVersion( (POSVERSIONINFOW)&VersionInfo );
  1751. if( !NT_SUCCESS(Status) ) {
  1752. IF_SAC_DEBUG(
  1753. SAC_DEBUG_FUNC_TRACE,
  1754. KdPrint(("SAC InitializeMachineInformation: Exiting (2).\n"))
  1755. );
  1756. goto InitializeMachineInformation_Failure;
  1757. }
  1758. //
  1759. // See if we're in gui-mode setup. We may need this info later.
  1760. //
  1761. Status = GetRegistryValueBuffer(
  1762. SETUP_KEY_NAME,
  1763. SETUPINPROGRESS_VALUE_NAME,
  1764. &ValueBuffer
  1765. );
  1766. if( NT_SUCCESS(Status) ) {
  1767. //
  1768. // See if it's 0 (we're not in Setup) or non-zero (we're in Setup)
  1769. //
  1770. if( *((PULONG)(ValueBuffer->Data)) != 0 ) {
  1771. InGuiModeSetup = TRUE;
  1772. }
  1773. FREE_POOL(&ValueBuffer);
  1774. }
  1775. //
  1776. // ========
  1777. // Machine name.
  1778. // ========
  1779. //
  1780. if( InGuiModeSetup ) {
  1781. //
  1782. // The machine name hasn't been initialized by the Setup process,
  1783. // so use some predefined string for the manchine name.
  1784. //
  1785. MachineInformation->MachineName = ALLOCATE_POOL(((ULONG)wcslen((PWSTR)GetMessage(SAC_DEFAULT_MACHINENAME))+1) * sizeof(WCHAR), GENERAL_POOL_TAG);
  1786. if( MachineInformation->MachineName ) {
  1787. wcscpy( MachineInformation->MachineName, GetMessage(SAC_DEFAULT_MACHINENAME) );
  1788. }
  1789. } else {
  1790. //
  1791. // We are not in Guimode setup, so go dig the machinename
  1792. // out of the registry.
  1793. //
  1794. Status = GetRegistryValueBuffer(
  1795. COMPUTERNAME_KEY_NAME,
  1796. COMPUTERNAME_VALUE_NAME,
  1797. &ValueBuffer
  1798. );
  1799. if( NT_SUCCESS(Status) ) {
  1800. //
  1801. // we successfully retrieved the machine name
  1802. //
  1803. Status = CopyRegistryValueData(
  1804. &(MachineInformation->MachineName),
  1805. ValueBuffer
  1806. );
  1807. FREE_POOL(&ValueBuffer);
  1808. if( !NT_SUCCESS(Status) ) {
  1809. IF_SAC_DEBUG(
  1810. SAC_DEBUG_FUNC_TRACE,
  1811. KdPrint(("SAC InitializeMachineInformation: Exiting (20).\n"))
  1812. );
  1813. goto InitializeMachineInformation_Failure;
  1814. }
  1815. } else {
  1816. IF_SAC_DEBUG(
  1817. SAC_DEBUG_FUNC_TRACE,
  1818. KdPrint(("SAC InitializeMachineInformation: Failed to get machine name.\n"))
  1819. );
  1820. }
  1821. }
  1822. //
  1823. // ========
  1824. // Machine GUID.
  1825. // ========
  1826. //
  1827. // make sure.
  1828. RtlZeroMemory( &MyGUID, sizeof(GUID) );
  1829. i = sizeof(GUID);
  1830. Status = HeadlessDispatch( HeadlessCmdQueryGUID,
  1831. NULL,
  1832. 0,
  1833. &MyGUID,
  1834. &i );
  1835. if( NT_SUCCESS(Status) ) {
  1836. MyTemporaryBufferW = (PWSTR)ALLOCATE_POOL( ((sizeof(GUID)*2) + 8) * sizeof(WCHAR) , GENERAL_POOL_TAG );
  1837. if( MyTemporaryBufferW == NULL ) {
  1838. IF_SAC_DEBUG(
  1839. SAC_DEBUG_FUNC_TRACE,
  1840. KdPrint(("SAC InitializeMachineInformation: Exiting (31).\n"))
  1841. );
  1842. goto InitializeMachineInformation_Failure;
  1843. }
  1844. swprintf( MyTemporaryBufferW,
  1845. L"%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
  1846. MyGUID.Data1,
  1847. MyGUID.Data2,
  1848. MyGUID.Data3,
  1849. MyGUID.Data4[0],
  1850. MyGUID.Data4[1],
  1851. MyGUID.Data4[2],
  1852. MyGUID.Data4[3],
  1853. MyGUID.Data4[4],
  1854. MyGUID.Data4[5],
  1855. MyGUID.Data4[6],
  1856. MyGUID.Data4[7] );
  1857. MachineInformation->GUID = MyTemporaryBufferW;
  1858. } else {
  1859. IF_SAC_DEBUG(
  1860. SAC_DEBUG_FUNC_TRACE,
  1861. KdPrint(("SAC InitializeMachineInformation: Failed to get Machine GUID.\n"))
  1862. );
  1863. }
  1864. //
  1865. // ========
  1866. // Processor Architecture.
  1867. // ========
  1868. //
  1869. Status = GetRegistryValueBuffer(
  1870. PROCESSOR_ARCHITECTURE_KEY_NAME,
  1871. PROCESSOR_ARCHITECTURE_VALUE_NAME,
  1872. &ValueBuffer
  1873. );
  1874. if( NT_SUCCESS(Status) ) {
  1875. Status = CopyRegistryValueData(
  1876. &(MachineInformation->ProcessorArchitecture),
  1877. ValueBuffer
  1878. );
  1879. FREE_POOL(&ValueBuffer);
  1880. if( !NT_SUCCESS(Status) ) {
  1881. IF_SAC_DEBUG(
  1882. SAC_DEBUG_FUNC_TRACE,
  1883. KdPrint(("SAC InitializeMachineInformation: Exiting (30).\n"))
  1884. );
  1885. goto InitializeMachineInformation_Failure;
  1886. }
  1887. } else {
  1888. IF_SAC_DEBUG(
  1889. SAC_DEBUG_FUNC_TRACE,
  1890. KdPrint(("SAC InitializeMachineInformation: Exiting (30).\n"))
  1891. );
  1892. }
  1893. //
  1894. // ========
  1895. // OS Name.
  1896. // ========
  1897. //
  1898. //
  1899. // Allocate enough memory for the formatting message, plus the size of 2 digits.
  1900. // Currently, our versioning info is of the type "5.1", so we don't need much space
  1901. // here, but let's be conservative and assume both major and minor version numbers
  1902. // are 5 digits in size. That's 11 characters.
  1903. //
  1904. // allow xxxxx.xxxxx
  1905. //
  1906. MyTemporaryBufferW = (PWSTR)ALLOCATE_POOL( (5 + 1 + 5 + 1) * sizeof(WCHAR), GENERAL_POOL_TAG );
  1907. if( MyTemporaryBufferW == NULL ) {
  1908. IF_SAC_DEBUG(
  1909. SAC_DEBUG_FUNC_TRACE,
  1910. KdPrint(("SAC InitializeMachineInformation: Exiting (50).\n"))
  1911. );
  1912. goto InitializeMachineInformation_Failure;
  1913. }
  1914. swprintf( MyTemporaryBufferW,
  1915. L"%d.%d",
  1916. VersionInfo.dwMajorVersion,
  1917. VersionInfo.dwMinorVersion );
  1918. MachineInformation->OSVersion = MyTemporaryBufferW;
  1919. //
  1920. // ========
  1921. // Build Number.
  1922. // ========
  1923. //
  1924. //
  1925. // Allocate enough memory for the formatting message, plus the size of our build number.
  1926. // Currently that's well below the 5-digit mark, but let's build some headroom here for
  1927. // build numbers up to 99000 (5-digits).
  1928. //
  1929. MyTemporaryBufferW = (PWSTR)ALLOCATE_POOL( ( 5 + 1 ) * sizeof(WCHAR), GENERAL_POOL_TAG );
  1930. if( MyTemporaryBufferW == NULL ) {
  1931. IF_SAC_DEBUG(
  1932. SAC_DEBUG_FUNC_TRACE,
  1933. KdPrint(("SAC InitializeMachineInformation: Exiting (60).\n"))
  1934. );
  1935. goto InitializeMachineInformation_Failure;
  1936. }
  1937. swprintf( MyTemporaryBufferW,
  1938. L"%d",
  1939. VersionInfo.dwBuildNumber );
  1940. MachineInformation->OSBuildNumber = MyTemporaryBufferW;
  1941. //
  1942. // ========
  1943. // Product Type (and Suite).
  1944. // ========
  1945. //
  1946. if( ExVerifySuite(DataCenter) ) {
  1947. pwStr = (PWSTR)GetMessage(SAC_MACHINEINFO_DATACENTER);
  1948. } else if( ExVerifySuite(EmbeddedNT) ) {
  1949. pwStr = GetMessage(SAC_MACHINEINFO_EMBEDDED);
  1950. } else if( ExVerifySuite(Enterprise) ) {
  1951. pwStr = (PWSTR)GetMessage(SAC_MACHINEINFO_ADVSERVER);
  1952. } else {
  1953. //
  1954. // We found no product suite that we recognized or cared about.
  1955. // Assume we're running on a generic server.
  1956. //
  1957. pwStr = (PWSTR)GetMessage(SAC_MACHINEINFO_SERVER);
  1958. }
  1959. //
  1960. // If we got a product type string message, then use this as our product type
  1961. //
  1962. if (pwStr) {
  1963. ULONG Size;
  1964. Size = (ULONG)((wcslen(pwStr) + 1) * sizeof(WCHAR));
  1965. ASSERT(Size > 0);
  1966. MachineInformation->OSProductType = (PWSTR)ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1967. if (MachineInformation->OSProductType == NULL) {
  1968. IF_SAC_DEBUG(
  1969. SAC_DEBUG_FAILS,
  1970. KdPrint(("SAC InitializeMachineInformation: Failed product type memory allocation.\n"))
  1971. );
  1972. goto InitializeMachineInformation_Failure;
  1973. }
  1974. RtlCopyMemory(MachineInformation->OSProductType, pwStr, Size);
  1975. } else {
  1976. IF_SAC_DEBUG(
  1977. SAC_DEBUG_FAILS,
  1978. KdPrint(("SAC InitializeMachineInformation: Failed to get product type.\n"))
  1979. );
  1980. }
  1981. //
  1982. // ========
  1983. // Service Pack Information.
  1984. // ========
  1985. //
  1986. if( VersionInfo.wServicePackMajor != 0 ) {
  1987. //
  1988. // There's been a service pack applied. Better tell the user.
  1989. //
  1990. //
  1991. // Allocate enough memory for the formatting message, plus the size of our servicepack number.
  1992. // Currently that's well below the 5-digit mark, but let's build some headroom here for
  1993. // service pack numbers up to 99000 (5-digits).
  1994. //
  1995. // allow for xxxxx.xxxxx
  1996. //
  1997. MyTemporaryBufferW = (PWSTR)ALLOCATE_POOL( (5 + 1 + 5 + 1) * sizeof(WCHAR), GENERAL_POOL_TAG );
  1998. if( MyTemporaryBufferW == NULL ) {
  1999. IF_SAC_DEBUG(
  2000. SAC_DEBUG_FAILS,
  2001. KdPrint(("SAC InitializeMachineInformation: Failed service pack memory allocation.\n"))
  2002. );
  2003. goto InitializeMachineInformation_Failure;
  2004. }
  2005. swprintf( MyTemporaryBufferW,
  2006. L"%d.%d",
  2007. VersionInfo.wServicePackMajor,
  2008. VersionInfo.wServicePackMinor );
  2009. MachineInformation->OSServicePack = MyTemporaryBufferW;
  2010. } else {
  2011. ULONG Size;
  2012. pwStr = (PWSTR)GetMessage(SAC_MACHINEINFO_NO_SERVICE_PACK);
  2013. Size = (ULONG)((wcslen(pwStr) + 1) * sizeof(WCHAR));
  2014. ASSERT(Size > 0);
  2015. MachineInformation->OSServicePack = (PWSTR)ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  2016. if (MachineInformation->OSServicePack == NULL) {
  2017. IF_SAC_DEBUG(
  2018. SAC_DEBUG_FAILS,
  2019. KdPrint(("SAC InitializeMachineInformation: Failed service pack memory allocation.\n"))
  2020. );
  2021. goto InitializeMachineInformation_Failure;
  2022. }
  2023. RtlCopyMemory(MachineInformation->OSServicePack, pwStr, Size);
  2024. }
  2025. return;
  2026. InitializeMachineInformation_Failure:
  2027. if( MachineInformation != NULL ) {
  2028. FREE_POOL(&MachineInformation);
  2029. MachineInformation = NULL;
  2030. }
  2031. IF_SAC_DEBUG(
  2032. SAC_DEBUG_FUNC_TRACE,
  2033. KdPrint(("SAC Initialize Machine Information: Exiting with error.\n"))
  2034. );
  2035. return;
  2036. }
  2037. NTSTATUS
  2038. SerialBufferGetChar(
  2039. IN PUCHAR ch
  2040. )
  2041. /*++
  2042. Routine Description:
  2043. This routine reads a character from the serial port buffer
  2044. which is populated by the TimerDPC function. The character
  2045. is read from the Consumer index position in the buffer. After
  2046. the character is read, the buffer position is nulled.
  2047. Arguments:
  2048. ch - on success, contains the character at the consumer index
  2049. Return Value:
  2050. Status
  2051. --*/
  2052. {
  2053. NTSTATUS Status;
  2054. Status = STATUS_SUCCESS;
  2055. do {
  2056. //
  2057. // Bail if there are no new characters to read
  2058. //
  2059. if (SerialPortConsumerIndex == SerialPortProducerIndex) {
  2060. Status = STATUS_NO_DATA_DETECTED;
  2061. break;
  2062. }
  2063. //
  2064. // Note: the following block is not done with an interlocked
  2065. // exchange because we don't need to. The design
  2066. // of the serialport ring buffer is such that the
  2067. // producer index is allowed to pass the consumer.
  2068. // This should not happen, however, because the
  2069. // consumer is notified whenever we get new data
  2070. // and the buffer should be large enough to allow
  2071. // for reasonable consumer delays.
  2072. //
  2073. {
  2074. //
  2075. // get the current character at the current consumer index.
  2076. //
  2077. *ch = SerialPortBuffer[SerialPortConsumerIndex];
  2078. //
  2079. // Null the value at the index we just read. This prevents
  2080. // information being present in the buffer after we've already
  2081. // read it.
  2082. //
  2083. SerialPortBuffer[SerialPortConsumerIndex] = 0;
  2084. }
  2085. //
  2086. // Compute the new producer index and store it atomically
  2087. //
  2088. InterlockedExchange(
  2089. (volatile long *)&SerialPortConsumerIndex,
  2090. (SerialPortConsumerIndex + 1) % SERIAL_PORT_BUFFER_LENGTH
  2091. );
  2092. } while ( FALSE );
  2093. return Status;
  2094. }
  2095. #if ENABLE_CMD_SESSION_PERMISSION_CHECKING
  2096. NTSTATUS
  2097. GetCommandConsoleLaunchingPermission(
  2098. OUT PBOOLEAN Permission
  2099. )
  2100. /*++
  2101. Routine Description:
  2102. This routine determines if command console sessions are allowed
  2103. to be launched.
  2104. Arguments:
  2105. Permission - on success, contains TRUE if sessions are allowed
  2106. to be launched
  2107. Return Value:
  2108. Status
  2109. --*/
  2110. {
  2111. NTSTATUS Status;
  2112. PWSTR KEY_NAME = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacdrv";
  2113. PWSTR VALUE_NAME = L"DisableCmdSessions";
  2114. PKEY_VALUE_PARTIAL_INFORMATION ValueBuffer;
  2115. //
  2116. // default: permission is granted unless we specifically find
  2117. // the key/value stating that it is not
  2118. //
  2119. *Permission = TRUE;
  2120. do {
  2121. //
  2122. // Attempt to find the registry key/value
  2123. //
  2124. Status = GetRegistryValueBuffer(
  2125. KEY_NAME,
  2126. VALUE_NAME,
  2127. &ValueBuffer
  2128. );
  2129. if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
  2130. //
  2131. // The reg key was not found, so the feature is enabled.
  2132. //
  2133. Status = STATUS_SUCCESS;
  2134. break;
  2135. }
  2136. if(! NT_SUCCESS(Status) ) {
  2137. break;
  2138. }
  2139. //
  2140. // We found the key/value, so notify the caller
  2141. // that permission has been denied.
  2142. //
  2143. *Permission = FALSE;
  2144. } while ( FALSE );
  2145. return Status;
  2146. }
  2147. #if ENABLE_SACSVR_START_TYPE_OVERRIDE
  2148. NTSTATUS
  2149. ImposeSacCmdServiceStartTypePolicy(
  2150. VOID
  2151. )
  2152. /*++
  2153. Routine Description:
  2154. This routine implement the service start type policy
  2155. that is imposed when the cmd console session feature
  2156. is ENABLED.
  2157. Here is the state table:
  2158. Command Console Feature Enabled:
  2159. service start type:
  2160. automatic --> NOP
  2161. manual --> automatic
  2162. disabled --> NOP
  2163. Arguments:
  2164. None
  2165. Return Value:
  2166. Status
  2167. --*/
  2168. {
  2169. NTSTATUS Status;
  2170. PWSTR KEY_NAME = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacsvr";
  2171. PWSTR VALUE_NAME = L"Start";
  2172. PULONG ValueData;
  2173. PKEY_VALUE_PARTIAL_INFORMATION ValueBuffer;
  2174. do {
  2175. //
  2176. // init
  2177. //
  2178. ValueBuffer = NULL;
  2179. //
  2180. // Attempt to find the registry key/value
  2181. //
  2182. Status = GetRegistryValueBuffer(
  2183. KEY_NAME,
  2184. VALUE_NAME,
  2185. &ValueBuffer
  2186. );
  2187. if(! NT_SUCCESS(Status)) {
  2188. break;
  2189. }
  2190. if(ValueBuffer == NULL) {
  2191. Status = STATUS_UNSUCCESSFUL;
  2192. break;
  2193. }
  2194. //
  2195. // Get the current start type value
  2196. //
  2197. Status = CopyRegistryValueData(
  2198. &ValueData,
  2199. ValueBuffer
  2200. );
  2201. FREE_POOL(&ValueBuffer);
  2202. if( !NT_SUCCESS(Status) ) {
  2203. break;
  2204. }
  2205. //
  2206. // Examine the current start type and assign
  2207. // a new type if appropriate.
  2208. //
  2209. switch (*ValueData) {
  2210. case 2: // automatic
  2211. case 4: // disabled
  2212. break;
  2213. case 3: // manual
  2214. //
  2215. // Set the start type --> Automatic
  2216. //
  2217. *ValueData = 2;
  2218. //
  2219. // Set the start type value in the service key.
  2220. //
  2221. Status = SetRegistryValue(
  2222. KEY_NAME,
  2223. VALUE_NAME,
  2224. REG_DWORD,
  2225. ValueData,
  2226. sizeof(ULONG)
  2227. );
  2228. if(!NT_SUCCESS(Status)) {
  2229. IF_SAC_DEBUG(
  2230. SAC_DEBUG_FAILS,
  2231. KdPrint(("SAC ImposeSacCmdServiceStartTypePolicy: Failed SetRegistryValue: %X\n", Status))
  2232. );
  2233. }
  2234. break;
  2235. default:
  2236. ASSERT(0);
  2237. break;
  2238. }
  2239. } while ( FALSE );
  2240. return Status;
  2241. }
  2242. #endif
  2243. #endif
  2244. NTSTATUS
  2245. CopyAndInsertStringAtInterval(
  2246. IN PWCHAR SourceStr,
  2247. IN ULONG Interval,
  2248. IN PWCHAR InsertStr,
  2249. OUT PWCHAR *pDestStr
  2250. )
  2251. /*++
  2252. Routine Description:
  2253. This routine takes a source string and inserts an
  2254. "interval string" at interval characters in the new
  2255. destination string.
  2256. Note: caller is responsible for releasing DestStr if successful
  2257. ex:
  2258. src "aaabbbccc"
  2259. interval string = "XYZ"
  2260. interval = 3
  2261. ==> dest string == "aaaXYZbbbXYZccc"
  2262. Arguments:
  2263. SourceStr - the source string
  2264. Interval - spanning interval
  2265. InsertStr - the insert string
  2266. DestStr - the destination string
  2267. Return Value:
  2268. Status
  2269. --*/
  2270. {
  2271. ULONG SrcLength;
  2272. ULONG DestLength;
  2273. ULONG DestSize;
  2274. ULONG InsertLength;
  2275. ULONG k;
  2276. ULONG l;
  2277. ULONG i;
  2278. PWCHAR DestStr;
  2279. ULONG IntervalCnt;
  2280. ASSERT_STATUS(SourceStr, STATUS_INVALID_PARAMETER_1);
  2281. ASSERT_STATUS(Interval > 0, STATUS_INVALID_PARAMETER_2);
  2282. ASSERT_STATUS(InsertStr, STATUS_INVALID_PARAMETER_3);
  2283. ASSERT_STATUS(pDestStr > 0, STATUS_INVALID_PARAMETER_4);
  2284. //
  2285. // the length of the insert string
  2286. //
  2287. InsertLength = (ULONG)wcslen(InsertStr);
  2288. //
  2289. // Compute how large the destination string needs to be,
  2290. // including the source string and the interval strings.
  2291. //
  2292. SrcLength = (ULONG)wcslen(SourceStr);
  2293. IntervalCnt = SrcLength / Interval;
  2294. if (SrcLength % Interval == 0) {
  2295. IntervalCnt = IntervalCnt > 0 ? IntervalCnt - 1 : IntervalCnt;
  2296. }
  2297. DestLength = SrcLength + (IntervalCnt * (ULONG)wcslen(InsertStr));
  2298. DestSize = (ULONG)((DestLength + 1) * sizeof(WCHAR));
  2299. //
  2300. // Allocate the new destination string
  2301. //
  2302. DestStr = ALLOCATE_POOL(DestSize, GENERAL_POOL_TAG);
  2303. ASSERT_STATUS(DestStr, STATUS_NO_MEMORY);
  2304. RtlZeroMemory(DestStr, DestSize);
  2305. //
  2306. // Initialize the pointers into the source and destination strings
  2307. //
  2308. l = 0;
  2309. i = 0;
  2310. do {
  2311. //
  2312. // k = # of characters to copy
  2313. //
  2314. // if Interval > # of characters left to copy,
  2315. // then k = # of characters left to copy
  2316. // else k = interval
  2317. //
  2318. k = Interval > (SrcLength - i) ? (SrcLength - i) : Interval;
  2319. //
  2320. // Copy k charactars to the destination buffer
  2321. //
  2322. wcsncpy(
  2323. &DestStr[l],
  2324. &SourceStr[i],
  2325. k
  2326. );
  2327. //
  2328. // Account for how many characters we just copied
  2329. //
  2330. l += k;
  2331. i += k;
  2332. //
  2333. // If there are any characters left to copy,
  2334. // then we need to insert the InsertString
  2335. // That is, we are at an interval.
  2336. //
  2337. if (i < SrcLength) {
  2338. //
  2339. // Insert the specified string at the interval
  2340. //
  2341. wcscpy(
  2342. &DestStr[l],
  2343. InsertStr
  2344. );
  2345. //
  2346. // Account for how many characters we just copied
  2347. //
  2348. l += InsertLength;
  2349. }
  2350. } while ( i < SrcLength);
  2351. //
  2352. //
  2353. //
  2354. ASSERT(i == SrcLength);
  2355. ASSERT(l == DestLength);
  2356. ASSERT((l + 1) * sizeof(WCHAR) == DestSize);
  2357. //
  2358. // Send back the destination string
  2359. //
  2360. *pDestStr = DestStr;
  2361. return STATUS_SUCCESS;
  2362. }
  2363. ULONG
  2364. GetMessageLineCount(
  2365. ULONG MessageId
  2366. )
  2367. /*++
  2368. Routine Description:
  2369. This routine retrieves a message resource and counts the # of lines in it
  2370. Arguments:
  2371. MessageId - The message id of the resource to send
  2372. Return Value:
  2373. LineCount
  2374. --*/
  2375. {
  2376. PCWSTR p;
  2377. ULONG c;
  2378. //
  2379. // we start at 0
  2380. // 1. if the message is found,
  2381. // then we know resource messages always have at least 1 CRLF
  2382. // 2. if the message is not found,
  2383. // then the line count is 0
  2384. //
  2385. c = 0;
  2386. p = GetMessage(MessageId);
  2387. if (p) {
  2388. while(*p) {
  2389. if (*p == L'\n') {
  2390. c++;
  2391. }
  2392. p++;
  2393. }
  2394. }
  2395. return(c);
  2396. }