xref: /reactos/drivers/sac/driver/util.c (revision 50cf16b3)
1 /*
2  * PROJECT:     ReactOS Drivers
3  * LICENSE:     BSD - See COPYING.ARM in the top level directory
4  * FILE:        drivers/sac/driver/util.c
5  * PURPOSE:     Driver for the Server Administration Console (SAC) for EMS
6  * PROGRAMMERS: ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "sacdrv.h"
12 
13 #include <ndk/rtlfuncs.h>
14 
15 /* GLOBALS ********************************************************************/
16 
17 PCHAR Utf8ConversionBuffer;
18 ULONG Utf8ConversionBufferSize = PAGE_SIZE;
19 
20 PSAC_MACHINE_INFO MachineInformation;
21 
22 PVOID RequestSacCmdEventObjectBody;
23 PKEVENT RequestSacCmdEventWaitObjectBody;
24 PVOID RequestSacCmdSuccessEventObjectBody;
25 PKEVENT RequestSacCmdSuccessEventWaitObjectBody;
26 PVOID RequestSacCmdFailureEventObjectBody;
27 PKEVENT RequestSacCmdFailureEventWaitObjectBody;
28 PFILE_OBJECT ServiceProcessFileObject;
29 BOOLEAN HaveUserModeServiceCmdEventInfo;
30 
31 PSAC_MESSAGE_ENTRY GlobalMessageTable;
32 ULONG GlobalMessageTableCount;
33 
34 LONG SerialPortConsumerIndex, SerialPortProducerIndex;
35 PCHAR SerialPortBuffer;
36 
37 /* FUNCTIONS ******************************************************************/
38 
39 BOOLEAN
40 NTAPI
41 SacTranslateUtf8ToUnicode(IN CHAR Utf8Char,
42                           IN PCHAR Utf8Buffer,
43                           OUT PWCHAR Utf8Value)
44 {
45     ULONG i;
46 
47     /* Find out how many valid characters we have in the buffer */
48     i = 0;
49     while (Utf8Buffer[i++] && (i < 3));
50 
51     /* If we have at least 3, shift everything by a byte */
52     if (i >= 3)
53     {
54         /* The last input character goes at the end */
55         Utf8Buffer[0] = Utf8Buffer[1];
56         Utf8Buffer[1] = Utf8Buffer[2];
57         Utf8Buffer[2] = Utf8Char;
58     }
59     else
60     {
61         /* We don't have more than 3 characters, place the input at the index */
62         Utf8Buffer[i] = Utf8Char;
63     }
64 
65     /* Print to debugger */
66     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SacTranslateUtf8ToUnicode - About to decode the UTF8 buffer.\n");
67     SAC_DBG(SAC_DBG_ENTRY_EXIT, "                                  UTF8[0]: 0x%02lx UTF8[1]: 0x%02lx UTF8[2]: 0x%02lx\n",
68             Utf8Buffer[0],
69             Utf8Buffer[1],
70             Utf8Buffer[2]);
71 
72     /* Is this a simple ANSI character? */
73     if (!(Utf8Char & 0x80))
74     {
75         /* Return it as Unicode, nothing left to do */
76         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SACDRV: SacTranslateUTf8ToUnicode - Case1\n");
77         *Utf8Value = (WCHAR)Utf8Char;
78         Utf8Buffer[0] = Utf8Buffer[1];
79         Utf8Buffer[1] = Utf8Buffer[2];
80         Utf8Buffer[2] = UNICODE_NULL;
81         return TRUE;
82     }
83 
84     /* Anything else is not yet supported */
85     ASSERT(FALSE);
86     return FALSE;
87 }
88 
89 BOOLEAN
90 NTAPI
91 SacTranslateUnicodeToUtf8(IN PWCHAR SourceBuffer,
92                           IN ULONG SourceBufferLength,
93                           OUT PCHAR DestinationBuffer,
94                           IN ULONG DestinationBufferSize,
95                           OUT PULONG UTF8Count,
96                           OUT PULONG ProcessedCount)
97 {
98     *UTF8Count = 0;
99     *ProcessedCount = 0;
100 
101     while ((*SourceBuffer) &&
102            (*UTF8Count < DestinationBufferSize) &&
103            (*ProcessedCount < SourceBufferLength))
104     {
105         if (*SourceBuffer & 0xFF80)
106         {
107             if (*SourceBuffer & 0xF800)
108             {
109                 if ((*UTF8Count + 3) >= DestinationBufferSize) break;
110                 DestinationBuffer[*UTF8Count] = ((*SourceBuffer >> 12) & 0xF) | 0xE0;
111                 ++*UTF8Count;
112                 DestinationBuffer[*UTF8Count] = ((*SourceBuffer >> 6) & 0x3F) | 0x80;
113             }
114             else
115             {
116                 if ((*UTF8Count + 2) >= DestinationBufferSize) break;
117                 DestinationBuffer[*UTF8Count] = ((*SourceBuffer >> 6) & 31) | 0xC0;
118             }
119             ++*UTF8Count;
120             DestinationBuffer[*UTF8Count] = (*SourceBuffer & 0x3F) | 0x80;
121         }
122         else
123         {
124             DestinationBuffer[*UTF8Count] = (*SourceBuffer & 0x7F);
125         }
126 
127         ++*UTF8Count;
128         ++*ProcessedCount;
129         ++SourceBuffer;
130     }
131 
132     ASSERT(*ProcessedCount <= SourceBufferLength);
133     ASSERT(*UTF8Count <= DestinationBufferSize);
134     return TRUE;
135 }
136 
137 PWCHAR
138 NTAPI
139 GetMessage(IN ULONG MessageIndex)
140 {
141     PSAC_MESSAGE_ENTRY MessageEntry;
142     ULONG i;
143     PWCHAR MessageData = NULL;
144 
145     /* Loop all cached messages */
146     for (i = 0; i < GlobalMessageTableCount; i++)
147     {
148         /* Check if this one matches the index */
149         MessageEntry = &GlobalMessageTable[i];
150         if (MessageEntry->Index == MessageIndex)
151         {
152             /* It does, return the buffer */
153             MessageData = MessageEntry->Buffer;
154             break;
155         }
156     }
157 
158     /* We should always find it */
159     if (!MessageData) ASSERT(FALSE);
160     return MessageData;
161 }
162 
163 NTSTATUS
164 NTAPI
165 UTF8EncodeAndSend(IN PWCHAR String)
166 {
167     ULONG ProcessedCount, Utf8Count, i;
168     NTSTATUS Status = STATUS_SUCCESS;
169 
170     /* Call the translator routine */
171     if (SacTranslateUnicodeToUtf8(String,
172                                   wcslen(String),
173                                   Utf8ConversionBuffer,
174                                   Utf8ConversionBufferSize,
175                                   &Utf8Count,
176                                   &ProcessedCount))
177     {
178         /* Loop every character */
179         for (i = 0; i < Utf8Count; i++)
180         {
181             /* Send it to the terminal */
182             Status = HeadlessDispatch(HeadlessCmdPutData,
183                                       &Utf8ConversionBuffer[i],
184                                       sizeof(Utf8ConversionBuffer[i]),
185                                       NULL,
186                                       NULL);
187             if (!NT_SUCCESS(Status)) break;
188         }
189     }
190     else
191     {
192         /* Conversion failed */
193         Status = STATUS_UNSUCCESSFUL;
194     }
195 
196     /* All done */
197     return Status;
198 }
199 
200 VOID
201 NTAPI
202 SacFormatMessage(IN PWCHAR FormattedString,
203                  IN PWCHAR MessageString,
204                  IN ULONG MessageSize)
205 {
206     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SacFormatMessage: Entering.\n");
207 
208     /* Check if any of the parameters are NULL or zero */
209     if (!(MessageString) || !(FormattedString) || !(MessageSize))
210     {
211         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SacFormatMessage: Exiting with invalid parameters.\n");
212         return;
213     }
214 
215     /* Keep going as long as there's still characters */
216     while ((MessageString[0]) && (MessageSize))
217     {
218         /* Is it a non-formatting character? */
219         if (MessageString[0] != L'%')
220         {
221             /* Just write it back into the buffer and keep going */
222             *FormattedString++ = MessageString[0];
223             MessageString++;
224         }
225         else
226         {
227             /* Go over the format characters we recognize */
228             switch (MessageString[1])
229             {
230                 case L'0':
231                     *FormattedString = UNICODE_NULL;
232                     return;
233 
234                 case L'%':
235                     *FormattedString++ = L'%';
236                     break;
237 
238                 case L'\\':
239                     *FormattedString++ = L'\r';
240                     *FormattedString++ = L'\n';
241                     break;
242 
243                 case L'r':
244                     *FormattedString++ = L'\r';
245                     break;
246 
247                 case L'b':
248                     *FormattedString++ = L' ';
249                     break;
250 
251                 case L'.':
252                     *FormattedString++ = L'.';
253                     break;
254 
255                 case L'!':
256                     *FormattedString++ = L'!';
257                     break;
258 
259                 default:
260                     /* Only move forward one character */
261                     MessageString--;
262                     break;
263             }
264 
265             /* Move forward two characters */
266             MessageString += 2;
267         }
268 
269         /* Move to the next character*/
270         MessageSize--;
271     }
272 
273     /* All done */
274     *FormattedString = UNICODE_NULL;
275     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SacFormatMessage: Exiting.\n");
276 }
277 
278 NTSTATUS
279 NTAPI
280 PreloadGlobalMessageTable(IN PVOID ImageBase)
281 {
282     NTSTATUS Status, Status2;
283     ULONG MessageId, TotalLength, TextSize, i;
284     PWCHAR StringBuffer;
285     PMESSAGE_RESOURCE_ENTRY MessageEntry;
286     PAGED_CODE();
287     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC PreloadGlobalMessageTable: Entering.\n");
288 
289     /* Nothing to do if we already have a table */
290     Status = STATUS_SUCCESS;
291     if (GlobalMessageTable) goto Exit;
292 
293     /* Loop through up to 200 messages */
294     TotalLength = 0;
295     for (MessageId = 1; MessageId != SAC_MAX_MESSAGES; MessageId++)
296     {
297         /* Find this message ID in the string table*/
298         Status2 = RtlFindMessage(ImageBase,
299                                  11,
300                                  LANG_NEUTRAL,
301                                  MessageId,
302                                  &MessageEntry);
303         if (NT_SUCCESS(Status2))
304         {
305             /* Make sure it's Unicode */
306             ASSERT(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE);
307 
308             /* Remove the space taken up by the OS header, and add our own */
309             TotalLength += MessageEntry->Length -
310                            FIELD_OFFSET(MESSAGE_RESOURCE_ENTRY, Text) +
311                            sizeof(SAC_MESSAGE_ENTRY);
312 
313             /* One more in the table */
314             GlobalMessageTableCount++;
315         }
316     }
317 
318     /* We should've found at least one message... */
319     if (!TotalLength)
320     {
321         /* Bail out otherwise */
322         SAC_DBG(SAC_DBG_INIT, "SAC PreloadGlobalMessageTable: No Messages.\n");
323         Status = STATUS_INVALID_PARAMETER;
324         goto Exit;
325     }
326 
327     /* Allocate space for the buffers and headers */
328     GlobalMessageTable = SacAllocatePool(TotalLength, GLOBAL_BLOCK_TAG);
329     if (!GlobalMessageTable)
330     {
331         /* Bail out if we couldn't allocate it */
332         Status = STATUS_NO_MEMORY;
333         goto Exit;
334     }
335 
336     /* All the buffers are going to be at the end of the table */
337     StringBuffer = (PWCHAR)(&GlobalMessageTable[GlobalMessageTableCount]);
338 
339     /* Now loop over our entries again */
340     for (i = 0, MessageId = 1; MessageId != SAC_MAX_MESSAGES; MessageId++)
341     {
342         /* Make sure the message is still there...! */
343         Status2 = RtlFindMessage(ImageBase,
344                                  11,
345                                  LANG_NEUTRAL,
346                                  MessageId,
347                                  &MessageEntry);
348         if (NT_SUCCESS(Status2))
349         {
350             /* Write the entry in the message table*/
351             GlobalMessageTable[i].Index = MessageId;
352             GlobalMessageTable[i].Buffer = StringBuffer;
353 
354             /* The structure includes the size of the header, elide it */
355             TextSize = MessageEntry->Length -
356                        FIELD_OFFSET(MESSAGE_RESOURCE_ENTRY, Text);
357 
358             /* Format the message into the entry. It should be same or smaller */
359             SacFormatMessage(StringBuffer, (PWCHAR)MessageEntry->Text, TextSize);
360             ASSERT((ULONG)(wcslen(StringBuffer)*sizeof(WCHAR)) <= TextSize);
361 
362             /* Move to the next buffer space */
363             StringBuffer += (TextSize / sizeof(WCHAR));
364 
365             /* Move to the next entry, make sure the status is full success */
366             i++;
367             Status = STATUS_SUCCESS;
368         }
369     }
370 
371 Exit:
372     /* All done, return the status code */
373     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC TearDownGlobalMessageTable: Exiting with status 0x%0x\n", Status);
374     return Status;
375 }
376 
377 NTSTATUS
378 NTAPI
379 TearDownGlobalMessageTable(VOID)
380 {
381     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC TearDownGlobalMessageTable: Entering.\n");
382 
383     /* Free the table if one existed */
384     if (GlobalMessageTable) SacFreePool(GlobalMessageTable);
385 
386     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC TearDownGlobalMessageTable: Exiting\n");
387     return STATUS_SUCCESS;
388 }
389 
390 NTSTATUS
391 NTAPI
392 GetRegistryValueBuffer(IN PCWSTR KeyName,
393                        IN PWCHAR ValueName,
394                        IN PKEY_VALUE_PARTIAL_INFORMATION* Buffer)
395 {
396     NTSTATUS Status;
397     OBJECT_ATTRIBUTES ObjectAttributes;
398     UNICODE_STRING DestinationString;
399     HANDLE Handle;
400     ULONG ResultLength = 0;
401     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: Entering.\n");
402     CHECK_PARAMETER1(KeyName);
403     CHECK_PARAMETER2(ValueName);
404 
405     /* Open the specified key */
406     RtlInitUnicodeString(&DestinationString, KeyName);
407     InitializeObjectAttributes(&ObjectAttributes,
408                                &DestinationString,
409                                OBJ_CASE_INSENSITIVE,
410                                NULL,
411                                NULL);
412     Status = ZwOpenKey(&Handle,
413                        KEY_WRITE | SYNCHRONIZE | KEY_READ,
414                        &ObjectAttributes);
415     if (!NT_SUCCESS(Status))
416     {
417         /* Bail out on failure */
418         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: failed ZwOpenKey: %X.\n", Status);
419         return Status;
420     }
421 
422     /* Query the size of the key */
423     RtlInitUnicodeString(&DestinationString, ValueName);
424     Status = ZwQueryValueKey(Handle,
425                              &DestinationString,
426                              KeyValuePartialInformation,
427                              NULL,
428                              0,
429                              &ResultLength);
430     if (!ResultLength) return Status;
431 
432     /* Allocate the buffer for the partial info structure and our integer data */
433     ResultLength += sizeof(ULONG);
434     *Buffer = SacAllocatePool(ResultLength, GLOBAL_BLOCK_TAG);
435     if (!*Buffer)
436     {
437         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: failed allocation\n");
438         return Status;
439     }
440 
441     /* Now read the data */
442     Status = ZwQueryValueKey(Handle,
443                              &DestinationString,
444                              KeyValuePartialInformation,
445                              *Buffer,
446                              ResultLength,
447                              &ResultLength);
448     if (!NT_SUCCESS(Status))
449     {
450         /* Free the buffer if we couldn't read the data */
451         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: failed ZwQueryValueKey: %X.\n", Status);
452         SacFreePool(*Buffer);
453     }
454 
455     /* Return the result */
456     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: Exiting.\n");
457     return Status;
458 }
459 
460 NTSTATUS
461 NTAPI
462 SetRegistryValue(IN PCWSTR KeyName,
463                  IN PWCHAR ValueName,
464                  IN ULONG Type,
465                  IN PVOID Data,
466                  IN ULONG DataSize)
467 {
468     NTSTATUS Status;
469     OBJECT_ATTRIBUTES ObjectAttributes;
470     UNICODE_STRING DestinationString;
471     HANDLE Handle;
472     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: Entering.\n");
473     CHECK_PARAMETER1(KeyName);
474     CHECK_PARAMETER2(ValueName);
475     CHECK_PARAMETER4(Data);
476 
477     /* Open the specified key */
478     RtlInitUnicodeString(&DestinationString, KeyName);
479     InitializeObjectAttributes(&ObjectAttributes,
480                                &DestinationString,
481                                OBJ_CASE_INSENSITIVE,
482                                NULL,
483                                NULL);
484     Status = ZwOpenKey(&Handle,
485                        KEY_WRITE | SYNCHRONIZE | KEY_READ,
486                        &ObjectAttributes);
487     if (!NT_SUCCESS(Status))
488     {
489         /* Bail out on failure */
490         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: failed ZwOpenKey: %X.\n", Status);
491         return Status;
492     }
493 
494     /* Set the specified value */
495     RtlInitUnicodeString(&DestinationString, ValueName);
496     Status = ZwSetValueKey(Handle, &DestinationString, 0, Type, Data, DataSize);
497     if (!NT_SUCCESS(Status))
498     {
499         /* Print error on failure */
500         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: failed ZwSetValueKey: %X.\n", Status);
501     }
502 
503     /* Close the handle and exit */
504     NtClose(Handle);
505     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: Exiting.\n");
506     return Status;
507 }
508 
509 NTSTATUS
510 NTAPI
511 CopyRegistryValueData(IN PVOID* Buffer,
512                       IN PKEY_VALUE_PARTIAL_INFORMATION PartialInfo)
513 {
514     NTSTATUS Status = STATUS_SUCCESS;
515     CHECK_PARAMETER1(Buffer);
516     CHECK_PARAMETER2(PartialInfo);
517 
518     /* Allocate space for registry data */
519     *Buffer = SacAllocatePool(PartialInfo->DataLength, GLOBAL_BLOCK_TAG);
520     if (*Buffer)
521     {
522         /* Copy the data into the buffer */
523         RtlCopyMemory(*Buffer, PartialInfo->Data, PartialInfo->DataLength);
524     }
525     else
526     {
527         /* Set the correct error code */
528         SAC_DBG(SAC_DBG_UTIL, "SAC CopyRegistryValueBuffer: Failed ALLOCATE.\n");
529         Status = STATUS_NO_MEMORY;
530     }
531 
532     /* Return the result */
533     return Status;
534 }
535 
536 NTSTATUS
537 NTAPI
538 TranslateMachineInformationXML(IN PWCHAR *Buffer,
539                                IN PWCHAR ExtraData)
540 {
541     NTSTATUS Status;
542     SIZE_T Size;
543     PWCHAR p;
544     CHECK_PARAMETER1(Buffer);
545 
546     /* Start by believing the world is beautiful */
547     Status = STATUS_SUCCESS;
548 
549     /* First, the header */
550     Size = wcslen(L"<machine-info>\r\n");
551 
552     /* Do we have a machine name? */
553     if (MachineInformation->MachineName)
554     {
555         /* Go and add it in */
556         Size += wcslen(MachineInformation->MachineName);
557         Size += wcslen(L"<name>%s</name>\r\n");
558     }
559 
560     /* Do we have a GUID? */
561     if (MachineInformation->MachineGuid)
562     {
563         /* Go and add it in */
564         Size += wcslen(MachineInformation->MachineGuid);
565         Size += wcslen(L"<guid>%s</guid>\r\n");
566     }
567 
568     /* Do we know the processor? */
569     if (MachineInformation->ProcessorArchitecture)
570     {
571         /* Go and add it in */
572         Size += wcslen(MachineInformation->ProcessorArchitecture);
573         Size += wcslen(L"<processor-architecture>%s</processor-architecture>\r\n");
574     }
575 
576     /* Do we have the version? */
577     if (MachineInformation->MajorVersion)
578     {
579         /* Go and add it in */
580         Size += wcslen(MachineInformation->MajorVersion);
581         Size += wcslen(L"<os-version>%s</os-version>\r\n");
582     }
583 
584     /* Do we have the build? */
585     if (MachineInformation->BuildNumber)
586     {
587         /* Go and add it in */
588         Size += wcslen(MachineInformation->BuildNumber);
589         Size += wcslen(L"<os-build-number>%s</os-build-number>\r\n");
590     }
591 
592     /* Do we have the product type? */
593     if (MachineInformation->ProductType)
594     {
595         /* Go and add it in */
596         Size += wcslen(MachineInformation->ProductType);
597         Size += wcslen(L"<os-product>%s</os-product>\r\n");
598     }
599 
600     /* Do we have a service pack? */
601     if (MachineInformation->ServicePack)
602     {
603         /* Go and add it in */
604         Size += wcslen(MachineInformation->ServicePack);
605         Size += wcslen(L"<os-service-pack>%s</os-service-pack>\r\n");
606     }
607 
608     /* Anything else we need to know? Add it in too */
609     if (ExtraData) Size += wcslen(ExtraData);
610 
611     /* Finally, add the footer */
612     Size += wcslen(L"</machine-info>\r\n");
613 
614     /* Convert to bytes and add a NULL */
615     Size += sizeof(ANSI_NULL);
616     Size *= sizeof(WCHAR);
617 
618     /* Allocate space for the buffer */
619     p = SacAllocatePool(Size, GLOBAL_BLOCK_TAG);
620     *Buffer = p;
621     if (!p) return STATUS_NO_MEMORY;
622 
623     wcscpy(p, L"<machine-info>\r\n");
624     p += wcslen(L"<machine-info>\r\n");
625 
626     if (MachineInformation->MachineName)
627     {
628         p += swprintf(p,
629                       L"<name>%s</name>\r\n",
630                       MachineInformation->MachineName);
631     }
632 
633     if (MachineInformation->MachineGuid)
634     {
635         p += swprintf(p,
636                       L"<guid>%s</guid>\r\n",
637                       MachineInformation->MachineGuid);
638     }
639 
640     if (MachineInformation->ProcessorArchitecture)
641     {
642         p += swprintf(p,
643                       L"<processor-architecture>%s</processor-architecture>\r\n",
644                       MachineInformation->ProcessorArchitecture);
645     }
646 
647     if (MachineInformation->MajorVersion)
648     {
649         p += swprintf(p,
650                       L"<os-version>%s</os-version>\r\n",
651                       MachineInformation->MajorVersion);
652     }
653 
654     if (MachineInformation->BuildNumber)
655     {
656         p += swprintf(p,
657                       L"<os-build-number>%s</os-build-number>\r\n",
658                       MachineInformation->BuildNumber);
659     }
660 
661     if (MachineInformation->ProductType)
662     {
663         p += swprintf(p,
664                       L"<os-product>%s</os-product>\r\n",
665                       MachineInformation->ProductType);
666     }
667 
668     if (MachineInformation->ServicePack)
669     {
670         p += swprintf(p,
671                       L"<os-service-pack>%s</os-service-pack>\r\n",
672                       MachineInformation->ServicePack);
673     }
674 
675     if (ExtraData)
676     {
677         wcscpy(p, ExtraData);
678         p += wcslen(ExtraData);
679     }
680 
681     wcscpy(p, L"</machine-info>\r\n");
682     SAC_DBG(SAC_DBG_ENTRY_EXIT, "MachineInformation: %S\n", *Buffer);
683     ASSERT((((ULONG)wcslen(*Buffer) + 1) * sizeof(WCHAR)) <= Size);
684     return Status;
685 }
686 
687 VOID
688 NTAPI
689 InitializeMachineInformation(VOID)
690 {
691     NTSTATUS Status;
692     PWCHAR GuidString, MajorVersion, ServicePack, BuildNumber, MessageBuffer;
693     PWCHAR ProductType;
694     ULONG SuiteTypeMessage;
695     BOOLEAN SetupInProgress = FALSE;
696     GUID SystemGuid;
697     SIZE_T RealSize, Size, OutputSize;
698     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
699     RTL_OSVERSIONINFOEXW VersionInformation;
700     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC Initialize Machine Information : Entering.\n");
701 
702     /* Don't do anything if we already queried this */
703     if (MachineInformation)
704     {
705         SAC_DBG(SAC_DBG_MACHINE, "SAC Initialize Machine Information:: MachineInformationBuffer already initialized.\n");
706         return;
707     }
708 
709     /* Allocate the machine information */
710     MachineInformation = SacAllocatePool(sizeof(*MachineInformation),
711                                          GLOBAL_BLOCK_TAG);
712     if (!MachineInformation)
713     {
714         goto Fail;
715     }
716 
717     /* Zero it out for now */
718     RtlZeroMemory(MachineInformation, sizeof(*MachineInformation));
719 
720     /* Query OS version */
721     RtlZeroMemory(&VersionInformation, sizeof(VersionInformation));
722     VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
723     Status = RtlGetVersion((PRTL_OSVERSIONINFOW)&VersionInformation);
724     if (!NT_SUCCESS(Status))
725     {
726         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (2).\n");
727         goto Fail;
728     }
729 
730     /* Check if setup is in progress */
731     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\Setup",
732                                     L"SystemSetupInProgress",
733                                     &PartialInfo);
734     if (NT_SUCCESS(Status))
735     {
736         /* The key is there, is the value set? */
737         if (*(PULONG)PartialInfo->Data) SetupInProgress = TRUE;
738         SacFreePool(PartialInfo);
739         if (SetupInProgress)
740         {
741             /* Yes, so we'll use a special hostname to identify this */
742             MessageBuffer = GetMessage(SAC_UNINITIALIZED_MSG);
743             Size = wcslen(MessageBuffer);
744             ASSERT(Size > 0);
745             RealSize = Size * sizeof(WCHAR) + sizeof(UNICODE_NULL);
746 
747             /* Make room for it and copy it in there */
748             MachineInformation->MachineName = SacAllocatePool(RealSize,
749                                                               GLOBAL_BLOCK_TAG);
750             if (MachineInformation->MachineName)
751             {
752                 wcscpy(MachineInformation->MachineName, MessageBuffer);
753             }
754         }
755     }
756 
757     /* If we are not in setup mode, or if we failed to check... */
758     if (!SetupInProgress)
759     {
760         /* Query the computer name */
761         Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\"
762                                         L"CurrentControlSet\\Control\\"
763                                         L"ComputerName\\ComputerName",
764                                         L"ComputerName",
765                                         &PartialInfo);
766         if (!NT_SUCCESS(Status))
767         {
768             /* It's not critical, but we won't have it */
769             SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Failed to get machine name.\n");
770         }
771         else
772         {
773             /* We have the name, copy it from the registry */
774             Status = CopyRegistryValueData((PVOID*)&MachineInformation->
775                                            MachineName,
776                                            PartialInfo);
777             SacFreePool(PartialInfo);
778             if (!NT_SUCCESS(Status))
779             {
780                 SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (20).\n");
781                 goto Fail;
782             }
783         }
784     }
785 
786     /* Next step, try to get the machine GUID */
787     RtlZeroMemory(&SystemGuid, sizeof(SystemGuid));
788     OutputSize = sizeof(SystemGuid);
789     Status = HeadlessDispatch(HeadlessCmdQueryGUID,
790                               NULL,
791                               0,
792                               &SystemGuid,
793                               &OutputSize);
794     if (!NT_SUCCESS(Status))
795     {
796         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Failed to get Machine GUID.\n");
797     }
798     else
799     {
800         /* We have it -- make room for it */
801         GuidString = SacAllocatePool(0x50, GLOBAL_BLOCK_TAG);
802         if (!GuidString)
803         {
804             SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (31).\n");
805             goto Fail;
806         }
807 
808         /* Build the string with the GUID in it, and save the ppointer to it */
809         swprintf(GuidString,
810                  L"%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
811                  SystemGuid.Data1,
812                  SystemGuid.Data2,
813                  SystemGuid.Data3,
814                  SystemGuid.Data4[0],
815                  SystemGuid.Data4[1],
816                  SystemGuid.Data4[2],
817                  SystemGuid.Data4[3],
818                  SystemGuid.Data4[4],
819                  SystemGuid.Data4[5],
820                  SystemGuid.Data4[6],
821                  SystemGuid.Data4[7]);
822         MachineInformation->MachineGuid = GuidString;
823     }
824 
825     /* Next, query the processor architecture */
826     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\"
827                                     L"CurrentControlSet\\Control\\"
828                                     L"Session Manager\\Environment",
829                                     L"PROCESSOR_ARCHITECTURE",
830                                     &PartialInfo);
831     if (!NT_SUCCESS(Status))
832     {
833         /* It's not critical, but we won't have it */
834         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (30).\n");
835     }
836     else
837     {
838         /* We have it! Copy the value from the registry */
839         Status = CopyRegistryValueData((PVOID*)&MachineInformation->
840                                        ProcessorArchitecture,
841                                        PartialInfo);
842         SacFreePool(PartialInfo);
843         if (!NT_SUCCESS(Status))
844         {
845             SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (30).\n");
846             goto Fail;
847         }
848     }
849 
850     /* Now allocate a buffer for the OS version number */
851     MajorVersion = SacAllocatePool(0x18, GLOBAL_BLOCK_TAG);
852     if (!MajorVersion)
853     {
854         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (50).\n");
855         goto Fail;
856     }
857 
858     /* Build the buffer and set a pointer to it */
859     swprintf(MajorVersion,
860              L"%d.%d",
861              VersionInformation.dwMajorVersion,
862              VersionInformation.dwMinorVersion);
863     MachineInformation->MajorVersion = MajorVersion;
864 
865     /* Now allocate a buffer for the OS build number */
866     BuildNumber = SacAllocatePool(0xC, GLOBAL_BLOCK_TAG);
867     if (!BuildNumber)
868     {
869         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (60).\n");
870         goto Fail;
871     }
872 
873     /* Build the buffer and set a pointer to it */
874     swprintf(BuildNumber, L"%d", VersionInformation.dwBuildNumber);
875     MachineInformation->BuildNumber = BuildNumber;
876 
877     /* Now check what kind of SKU this is */
878     if (ExVerifySuite(DataCenter))
879     {
880         SuiteTypeMessage = SAC_DATACENTER_SUITE_MSG;
881     }
882     else if (ExVerifySuite(EmbeddedNT))
883     {
884         SuiteTypeMessage = SAC_EMBEDDED_SUITE_MSG;
885     }
886     else if (ExVerifySuite(Enterprise))
887     {
888         SuiteTypeMessage = SAC_ENTERPRISE_SUITE_MSG;
889     }
890     else
891     {
892         /* Unknown or perhaps a client SKU */
893         SuiteTypeMessage = SAC_NO_SUITE_MSG;
894     }
895 
896     /* Get the string that corresponds to the SKU type */
897     MessageBuffer = GetMessage(SuiteTypeMessage);
898     if (!MessageBuffer)
899     {
900         /* We won't have it, but this isn't critical */
901         SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed to get product type.\n");
902     }
903     else
904     {
905         /* Calculate the size we need to hold the string */
906         Size = wcslen(MessageBuffer);
907         ASSERT(Size > 0);
908         RealSize = Size * sizeof(WCHAR) + sizeof(UNICODE_NULL);
909 
910         /* Allocate a buffer for it */
911         ProductType = SacAllocatePool(RealSize, GLOBAL_BLOCK_TAG);
912         if (!ProductType)
913         {
914             SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed product type memory allocation.\n");
915             goto Fail;
916         }
917 
918         /* Copy the string and set the pointer */
919         RtlCopyMemory(ProductType, MessageBuffer, RealSize);
920         MachineInformation->ProductType = ProductType;
921     }
922 
923     /* Check if this is a SP version or RTM version */
924     if (VersionInformation.wServicePackMajor)
925     {
926         /* This is a service pack, allocate a buffer for the version */
927         ServicePack = SacAllocatePool(0x18, GLOBAL_BLOCK_TAG);
928         if (ServicePack)
929         {
930             /* Build the buffer and set a pointer to it */
931             swprintf(ServicePack,
932                      L"%d.%d",
933                      VersionInformation.wServicePackMajor,
934                      VersionInformation.wServicePackMinor);
935             MachineInformation->ServicePack = ServicePack;
936 
937             /* We've collected all the machine info and are done! */
938             return;
939         }
940 
941         /* This is the failure path */
942         SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed service pack memory allocation.\n");
943     }
944     else
945     {
946         /* Get a generic string that indicates there's no service pack */
947         MessageBuffer = GetMessage(SAC_NO_DATA_MSG);
948         Size = wcslen(MessageBuffer);
949         ASSERT(Size > 0);
950         RealSize = Size * sizeof(WCHAR) + sizeof(UNICODE_NULL);
951 
952         /* Allocate memory for the "no service pack" string */
953         ServicePack = SacAllocatePool(RealSize, GLOBAL_BLOCK_TAG);
954         if (ServicePack)
955         {
956             /* Copy the buffer and set a pointer to it */
957             RtlCopyMemory(ServicePack, MessageBuffer, RealSize);
958             MachineInformation->ServicePack = ServicePack;
959 
960             /* We've collected all the machine info and are done! */
961             return;
962         }
963 
964         SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed service pack memory allocation.\n");
965     }
966 
967 Fail:
968     /* In the failure path, always cleanup the machine information buffer */
969     if (MachineInformation)
970     {
971         SacFreePool(MachineInformation);
972     }
973     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC Initialize Machine Information : Exiting with error.\n");
974 }
975 
976 NTSTATUS
977 NTAPI
978 GetCommandConsoleLaunchingPermission(OUT PBOOLEAN Permission)
979 {
980     NTSTATUS Status;
981     PKEY_VALUE_PARTIAL_INFORMATION Dummy;
982 
983     /* Assume success and read the key */
984     *Permission = TRUE;
985     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacdrv",
986                                     L"DisableCmdSessions",
987                                     &Dummy);
988     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
989     {
990         /* The default is success */
991         Status = STATUS_SUCCESS;
992     }
993     else
994     {
995         /* Only if the key is present and set, do we disable permission */
996         if (NT_SUCCESS(Status)) *Permission = FALSE;
997     }
998 
999     /* Return status */
1000     return Status;
1001 }
1002 
1003 NTSTATUS
1004 NTAPI
1005 ImposeSacCmdServiceStartTypePolicy(VOID)
1006 {
1007     NTSTATUS Status;
1008     PKEY_VALUE_PARTIAL_INFORMATION Buffer = NULL;
1009     PULONG Data;
1010 
1011     /* Read the service start type*/
1012     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacsvr",
1013                                     L"Start",
1014                                     &Buffer);
1015     if (!NT_SUCCESS(Status)) return Status;
1016 
1017     /* If there's no start type, fail, as this is unusual */
1018     if (!Buffer) return STATUS_UNSUCCESSFUL;
1019 
1020     /* Read the value */
1021     Status = CopyRegistryValueData((PVOID*)&Data, Buffer);
1022     SacFreePool(Buffer);
1023     if (!NT_SUCCESS(Status)) return Status;
1024 
1025     /* Check what the current start type is */
1026     switch (*Data)
1027     {
1028         /* It's boot, system, or disabled */
1029         case 1:
1030         case 2:
1031         case 4:
1032             /* Leave it as is */
1033             return Status;
1034 
1035         case 3:
1036 
1037             /* It's set to automatic, set it to system instead */
1038             *Data = 2;
1039             Status = SetRegistryValue(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacsvr",
1040                                       L"Start",
1041                                       REG_DWORD,
1042                                       Data,
1043                                       sizeof(ULONG));
1044             if (!NT_SUCCESS(Status))
1045             {
1046                 SAC_DBG(SAC_DBG_INIT, "SAC ImposeSacCmdServiceStartTypePolicy: Failed SetRegistryValue: %X\n", Status);
1047             }
1048             break;
1049 
1050         default:
1051             ASSERT(FALSE);
1052     }
1053 
1054     return Status;
1055 }
1056 
1057 VOID
1058 NTAPI
1059 InitializeCmdEventInfo(VOID)
1060 {
1061     /* Check if we were already initialized */
1062     if (HaveUserModeServiceCmdEventInfo)
1063     {
1064         /* Full state expected */
1065         ASSERT(RequestSacCmdEventObjectBody);
1066         ASSERT(RequestSacCmdSuccessEventObjectBody);
1067         ASSERT(RequestSacCmdFailureEventObjectBody);
1068 
1069         /* Dereference each wait object in turn */
1070         if (RequestSacCmdEventObjectBody)
1071         {
1072             ObDereferenceObject(RequestSacCmdEventObjectBody);
1073         }
1074 
1075         if (RequestSacCmdSuccessEventObjectBody)
1076         {
1077             ObDereferenceObject(RequestSacCmdSuccessEventObjectBody);
1078         }
1079 
1080         if (RequestSacCmdFailureEventObjectBody)
1081         {
1082             ObDereferenceObject(RequestSacCmdFailureEventObjectBody);
1083         }
1084     }
1085 
1086     /* Claer everything */
1087     RequestSacCmdEventObjectBody = NULL;
1088     RequestSacCmdEventWaitObjectBody = NULL;
1089     RequestSacCmdSuccessEventObjectBody = NULL;
1090     RequestSacCmdSuccessEventWaitObjectBody = NULL;
1091     RequestSacCmdFailureEventObjectBody = NULL;
1092     RequestSacCmdFailureEventWaitObjectBody = NULL;
1093     ServiceProcessFileObject = NULL;
1094 
1095     /* Reset state */
1096     HaveUserModeServiceCmdEventInfo = FALSE;
1097 }
1098 
1099 NTSTATUS
1100 NTAPI
1101 RegisterBlueScreenMachineInformation(VOID)
1102 {
1103     PWCHAR XmlBuffer;
1104     PHEADLESS_CMD_SET_BLUE_SCREEN_DATA BsBuffer;
1105     SIZE_T Length, HeaderLength, TotalLength;
1106     NTSTATUS Status;
1107     ULONG i;
1108 
1109     /* Create the XML buffer and make sure it's OK */
1110     Status = TranslateMachineInformationXML(&XmlBuffer, NULL);
1111     CHECK_PARAMETER_WITH_STATUS(NT_SUCCESS(Status), Status);
1112     CHECK_PARAMETER_WITH_STATUS(XmlBuffer, STATUS_UNSUCCESSFUL);
1113 
1114     /* Compute the sizes and allocate a buffer for it */
1115     Length = wcslen(XmlBuffer);
1116     HeaderLength = strlen("MACHINEINFO");
1117     TotalLength = HeaderLength +
1118                   Length +
1119                   sizeof(*BsBuffer) +
1120                   2 * sizeof(ANSI_NULL);
1121     BsBuffer = SacAllocatePool(TotalLength, GLOBAL_BLOCK_TAG);
1122     CHECK_PARAMETER_WITH_STATUS(BsBuffer, STATUS_NO_MEMORY);
1123 
1124     /* Copy the XML property name */
1125     strcpy((PCHAR)BsBuffer->Data, "MACHINEINFO");
1126     BsBuffer->ValueIndex = HeaderLength + sizeof(ANSI_NULL);
1127 
1128     /* Copy the data and NULL-terminate it */
1129     for (i = 0; i < Length; i++)
1130     {
1131         BsBuffer->Data[BsBuffer->ValueIndex + i] = XmlBuffer[i];
1132     }
1133     BsBuffer->Data[BsBuffer->ValueIndex + i] = ANSI_NULL;
1134 
1135     /* Let the OS save the buffer for later */
1136     Status = HeadlessDispatch(HeadlessCmdSetBlueScreenData,
1137                               BsBuffer,
1138                               TotalLength,
1139                               NULL,
1140                               NULL);
1141 
1142     /* Failure or not, we don't need this anymore */
1143     SacFreePool(BsBuffer);
1144     SacFreePool(XmlBuffer);
1145 
1146     /* Return the result */
1147     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC Initialize Machine Information: Exiting.\n");
1148     return Status;
1149 }
1150 
1151 VOID
1152 NTAPI
1153 FreeMachineInformation(VOID)
1154 {
1155     ASSERT(MachineInformation);
1156 
1157     /* Free every cached string of machine information */
1158     if (MachineInformation->MachineName) SacFreePool(MachineInformation);
1159     if (MachineInformation->MachineGuid) SacFreePool(MachineInformation->MachineGuid);
1160     if (MachineInformation->ProcessorArchitecture) SacFreePool(MachineInformation->ProcessorArchitecture);
1161     if (MachineInformation->MajorVersion) SacFreePool(MachineInformation->MajorVersion);
1162     if (MachineInformation->BuildNumber) SacFreePool(MachineInformation->BuildNumber);
1163     if (MachineInformation->ProductType) SacFreePool(MachineInformation->ProductType);
1164     if (MachineInformation->ServicePack) SacFreePool(MachineInformation->ServicePack);
1165 }
1166 
1167 BOOLEAN
1168 NTAPI
1169 VerifyEventWaitable(IN HANDLE Handle,
1170                     OUT PVOID *WaitObject,
1171                     OUT PVOID *ActualWaitObject)
1172 {
1173     PVOID Object;
1174     NTSTATUS Status;
1175     POBJECT_TYPE ObjectType;
1176 
1177     /* Reference the object */
1178     Status = ObReferenceObjectByHandle(Handle,
1179                                        EVENT_ALL_ACCESS,
1180                                        NULL,
1181                                        KernelMode,
1182                                        &Object,
1183                                        NULL);
1184     *WaitObject = Object;
1185     if (!NT_SUCCESS(Status))
1186     {
1187         SAC_DBG(SAC_DBG_INIT, "SAC VerifyEventWaitable: Unable to reference event object (%lx)\n", Status);
1188         return FALSE;
1189     }
1190 
1191     /* Check if the object itself is NOT being used */
1192     ObjectType = OBJECT_TO_OBJECT_HEADER(Object)->Type;
1193     if (ObjectType->TypeInfo.UseDefaultObject == FALSE)
1194     {
1195         /* Get the actual object that's being used for the wait */
1196         *ActualWaitObject = (PVOID)((ULONG_PTR)Object +
1197                                     (ULONG_PTR)ObjectType->DefaultObject);
1198         return TRUE;
1199     }
1200 
1201     /* Drop the reference we took */
1202     SAC_DBG(SAC_DBG_INIT, "SAC VerifyEventWaitable: event object not waitable!\n");
1203     ObDereferenceObject(*WaitObject);
1204     return FALSE;
1205 }
1206 
1207 NTSTATUS
1208 NTAPI
1209 SerialBufferGetChar(OUT PCHAR Char)
1210 {
1211     /* Check if nothing's been produced yet */
1212     if (SerialPortConsumerIndex == SerialPortProducerIndex)
1213     {
1214         return STATUS_NO_DATA_DETECTED;
1215     }
1216 
1217     /* Consume the produced character and clear it*/
1218     *Char = SerialPortBuffer[SerialPortConsumerIndex];
1219     SerialPortBuffer[SerialPortConsumerIndex] = ANSI_NULL;
1220 
1221     /* Advance the index and return success */
1222     _InterlockedExchange(&SerialPortConsumerIndex,
1223                          (SerialPortConsumerIndex + 1) &
1224                          (SAC_SERIAL_PORT_BUFFER_SIZE - 1));
1225     return STATUS_SUCCESS;
1226 }
1227 
1228 ULONG
1229 NTAPI
1230 GetMessageLineCount(IN ULONG MessageIndex)
1231 {
1232     ULONG LineCount = 0;
1233     PWCHAR Buffer;
1234 
1235     /* Get the message buffer */
1236     Buffer = GetMessage(MessageIndex);
1237     if (Buffer)
1238     {
1239         /* Scan it looking for new lines, and increment the count each time */
1240         while (*Buffer) if (*Buffer++ == L'\n') ++LineCount;
1241     }
1242 
1243     /* Return the line count */
1244     return LineCount;
1245 }
1246 
1247 ULONG
1248 ConvertAnsiToUnicode(
1249     IN PWCHAR pwch,
1250     IN PCHAR pch,
1251     IN ULONG length
1252     )
1253 {
1254     return STATUS_NOT_IMPLEMENTED;
1255 }
1256 
1257 BOOLEAN
1258 IsCmdEventRegistrationProcess(
1259     IN PFILE_OBJECT FileObject
1260     )
1261 {
1262     return FALSE;
1263 }
1264 
1265 NTSTATUS
1266 InvokeUserModeService(
1267     VOID
1268     )
1269 {
1270     return STATUS_NOT_IMPLEMENTED;
1271 }
1272 
1273 NTSTATUS
1274 TranslateMachineInformationText(
1275     IN PWCHAR Buffer)
1276 {
1277     return STATUS_NOT_IMPLEMENTED;
1278 }
1279 
1280 NTSTATUS
1281 CopyAndInsertStringAtInterval(
1282     IN PWCHAR SourceStr,
1283     IN ULONG Interval,
1284     IN PWCHAR InsertStr,
1285     OUT PWCHAR pDestStr
1286     )
1287 {
1288     return STATUS_NOT_IMPLEMENTED;
1289 }
1290 
1291 NTSTATUS
1292 RegisterSacCmdEvent(
1293     IN PVOID Object,
1294     IN PKEVENT SetupCmdEvent[]
1295     )
1296 {
1297     return STATUS_NOT_IMPLEMENTED;
1298 }
1299 
1300 NTSTATUS
1301 UnregisterSacCmdEvent(
1302     IN PFILE_OBJECT FileObject
1303     )
1304 {
1305     return STATUS_NOT_IMPLEMENTED;
1306 }
1307