xref: /reactos/drivers/sac/driver/util.c (revision bbccad0e)
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 | OBJ_KERNEL_HANDLE,
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)
431         goto Quit;
432 
433     /* Allocate the buffer for the partial info structure and our integer data */
434     ResultLength += sizeof(ULONG);
435     *Buffer = SacAllocatePool(ResultLength, GLOBAL_BLOCK_TAG);
436     if (!*Buffer)
437     {
438         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: failed allocation\n");
439         goto Quit;
440     }
441 
442     /* Now read the data */
443     Status = ZwQueryValueKey(Handle,
444                              &DestinationString,
445                              KeyValuePartialInformation,
446                              *Buffer,
447                              ResultLength,
448                              &ResultLength);
449     if (!NT_SUCCESS(Status))
450     {
451         /* Free the buffer if we couldn't read the data */
452         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: failed ZwQueryValueKey: %X.\n", Status);
453         SacFreePool(*Buffer);
454     }
455 
456 Quit:
457     /* Close the handle and exit */
458     ZwClose(Handle);
459     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC GetRegistryValueBuffer: Exiting.\n");
460     return Status;
461 }
462 
463 NTSTATUS
464 NTAPI
465 SetRegistryValue(IN PCWSTR KeyName,
466                  IN PWCHAR ValueName,
467                  IN ULONG Type,
468                  IN PVOID Data,
469                  IN ULONG DataSize)
470 {
471     NTSTATUS Status;
472     OBJECT_ATTRIBUTES ObjectAttributes;
473     UNICODE_STRING DestinationString;
474     HANDLE Handle;
475     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: Entering.\n");
476     CHECK_PARAMETER1(KeyName);
477     CHECK_PARAMETER2(ValueName);
478     CHECK_PARAMETER4(Data);
479 
480     /* Open the specified key */
481     RtlInitUnicodeString(&DestinationString, KeyName);
482     InitializeObjectAttributes(&ObjectAttributes,
483                                &DestinationString,
484                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
485                                NULL,
486                                NULL);
487     Status = ZwOpenKey(&Handle,
488                        KEY_WRITE | SYNCHRONIZE | KEY_READ,
489                        &ObjectAttributes);
490     if (!NT_SUCCESS(Status))
491     {
492         /* Bail out on failure */
493         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: failed ZwOpenKey: %X.\n", Status);
494         return Status;
495     }
496 
497     /* Set the specified value */
498     RtlInitUnicodeString(&DestinationString, ValueName);
499     Status = ZwSetValueKey(Handle, &DestinationString, 0, Type, Data, DataSize);
500     if (!NT_SUCCESS(Status))
501     {
502         /* Print error on failure */
503         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: failed ZwSetValueKey: %X.\n", Status);
504     }
505 
506     /* Close the handle and exit */
507     ZwClose(Handle);
508     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC SetRegistryValue: Exiting.\n");
509     return Status;
510 }
511 
512 NTSTATUS
513 NTAPI
514 CopyRegistryValueData(IN PVOID* Buffer,
515                       IN PKEY_VALUE_PARTIAL_INFORMATION PartialInfo)
516 {
517     NTSTATUS Status = STATUS_SUCCESS;
518     CHECK_PARAMETER1(Buffer);
519     CHECK_PARAMETER2(PartialInfo);
520 
521     /* Allocate space for registry data */
522     *Buffer = SacAllocatePool(PartialInfo->DataLength, GLOBAL_BLOCK_TAG);
523     if (*Buffer)
524     {
525         /* Copy the data into the buffer */
526         RtlCopyMemory(*Buffer, PartialInfo->Data, PartialInfo->DataLength);
527     }
528     else
529     {
530         /* Set the correct error code */
531         SAC_DBG(SAC_DBG_UTIL, "SAC CopyRegistryValueBuffer: Failed ALLOCATE.\n");
532         Status = STATUS_NO_MEMORY;
533     }
534 
535     /* Return the result */
536     return Status;
537 }
538 
539 NTSTATUS
540 NTAPI
541 TranslateMachineInformationXML(IN PWCHAR *Buffer,
542                                IN PWCHAR ExtraData)
543 {
544     NTSTATUS Status;
545     SIZE_T Size;
546     PWCHAR p;
547     CHECK_PARAMETER1(Buffer);
548 
549     /* Start by believing the world is beautiful */
550     Status = STATUS_SUCCESS;
551 
552     /* First, the header */
553     Size = wcslen(L"<machine-info>\r\n");
554 
555     /* Do we have a machine name? */
556     if (MachineInformation->MachineName)
557     {
558         /* Go and add it in */
559         Size += wcslen(MachineInformation->MachineName);
560         Size += wcslen(L"<name>%s</name>\r\n");
561     }
562 
563     /* Do we have a GUID? */
564     if (MachineInformation->MachineGuid)
565     {
566         /* Go and add it in */
567         Size += wcslen(MachineInformation->MachineGuid);
568         Size += wcslen(L"<guid>%s</guid>\r\n");
569     }
570 
571     /* Do we know the processor? */
572     if (MachineInformation->ProcessorArchitecture)
573     {
574         /* Go and add it in */
575         Size += wcslen(MachineInformation->ProcessorArchitecture);
576         Size += wcslen(L"<processor-architecture>%s</processor-architecture>\r\n");
577     }
578 
579     /* Do we have the version? */
580     if (MachineInformation->MajorVersion)
581     {
582         /* Go and add it in */
583         Size += wcslen(MachineInformation->MajorVersion);
584         Size += wcslen(L"<os-version>%s</os-version>\r\n");
585     }
586 
587     /* Do we have the build? */
588     if (MachineInformation->BuildNumber)
589     {
590         /* Go and add it in */
591         Size += wcslen(MachineInformation->BuildNumber);
592         Size += wcslen(L"<os-build-number>%s</os-build-number>\r\n");
593     }
594 
595     /* Do we have the product type? */
596     if (MachineInformation->ProductType)
597     {
598         /* Go and add it in */
599         Size += wcslen(MachineInformation->ProductType);
600         Size += wcslen(L"<os-product>%s</os-product>\r\n");
601     }
602 
603     /* Do we have a service pack? */
604     if (MachineInformation->ServicePack)
605     {
606         /* Go and add it in */
607         Size += wcslen(MachineInformation->ServicePack);
608         Size += wcslen(L"<os-service-pack>%s</os-service-pack>\r\n");
609     }
610 
611     /* Anything else we need to know? Add it in too */
612     if (ExtraData) Size += wcslen(ExtraData);
613 
614     /* Finally, add the footer */
615     Size += wcslen(L"</machine-info>\r\n");
616 
617     /* Convert to bytes and add a NULL */
618     Size += sizeof(ANSI_NULL);
619     Size *= sizeof(WCHAR);
620 
621     /* Allocate space for the buffer */
622     p = SacAllocatePool(Size, GLOBAL_BLOCK_TAG);
623     *Buffer = p;
624     if (!p) return STATUS_NO_MEMORY;
625 
626     wcscpy(p, L"<machine-info>\r\n");
627     p += wcslen(L"<machine-info>\r\n");
628 
629     if (MachineInformation->MachineName)
630     {
631         p += swprintf(p,
632                       L"<name>%s</name>\r\n",
633                       MachineInformation->MachineName);
634     }
635 
636     if (MachineInformation->MachineGuid)
637     {
638         p += swprintf(p,
639                       L"<guid>%s</guid>\r\n",
640                       MachineInformation->MachineGuid);
641     }
642 
643     if (MachineInformation->ProcessorArchitecture)
644     {
645         p += swprintf(p,
646                       L"<processor-architecture>%s</processor-architecture>\r\n",
647                       MachineInformation->ProcessorArchitecture);
648     }
649 
650     if (MachineInformation->MajorVersion)
651     {
652         p += swprintf(p,
653                       L"<os-version>%s</os-version>\r\n",
654                       MachineInformation->MajorVersion);
655     }
656 
657     if (MachineInformation->BuildNumber)
658     {
659         p += swprintf(p,
660                       L"<os-build-number>%s</os-build-number>\r\n",
661                       MachineInformation->BuildNumber);
662     }
663 
664     if (MachineInformation->ProductType)
665     {
666         p += swprintf(p,
667                       L"<os-product>%s</os-product>\r\n",
668                       MachineInformation->ProductType);
669     }
670 
671     if (MachineInformation->ServicePack)
672     {
673         p += swprintf(p,
674                       L"<os-service-pack>%s</os-service-pack>\r\n",
675                       MachineInformation->ServicePack);
676     }
677 
678     if (ExtraData)
679     {
680         wcscpy(p, ExtraData);
681         p += wcslen(ExtraData);
682     }
683 
684     wcscpy(p, L"</machine-info>\r\n");
685     SAC_DBG(SAC_DBG_ENTRY_EXIT, "MachineInformation: %S\n", *Buffer);
686     ASSERT((((ULONG)wcslen(*Buffer) + 1) * sizeof(WCHAR)) <= Size);
687     return Status;
688 }
689 
690 VOID
691 NTAPI
692 InitializeMachineInformation(VOID)
693 {
694     NTSTATUS Status;
695     PWCHAR GuidString, MajorVersion, ServicePack, BuildNumber, MessageBuffer;
696     PWCHAR ProductType;
697     ULONG SuiteTypeMessage;
698     BOOLEAN SetupInProgress = FALSE;
699     GUID SystemGuid;
700     SIZE_T RealSize, Size, OutputSize;
701     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
702     RTL_OSVERSIONINFOEXW VersionInformation;
703     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC Initialize Machine Information : Entering.\n");
704 
705     /* Don't do anything if we already queried this */
706     if (MachineInformation)
707     {
708         SAC_DBG(SAC_DBG_MACHINE, "SAC Initialize Machine Information:: MachineInformationBuffer already initialized.\n");
709         return;
710     }
711 
712     /* Allocate the machine information */
713     MachineInformation = SacAllocatePool(sizeof(*MachineInformation),
714                                          GLOBAL_BLOCK_TAG);
715     if (!MachineInformation)
716     {
717         goto Fail;
718     }
719 
720     /* Zero it out for now */
721     RtlZeroMemory(MachineInformation, sizeof(*MachineInformation));
722 
723     /* Query OS version */
724     RtlZeroMemory(&VersionInformation, sizeof(VersionInformation));
725     VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
726     Status = RtlGetVersion((PRTL_OSVERSIONINFOW)&VersionInformation);
727     if (!NT_SUCCESS(Status))
728     {
729         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (2).\n");
730         goto Fail;
731     }
732 
733     /* Check if setup is in progress */
734     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\Setup",
735                                     L"SystemSetupInProgress",
736                                     &PartialInfo);
737     if (NT_SUCCESS(Status))
738     {
739         /* The key is there, is the value set? */
740         if (*(PULONG)PartialInfo->Data) SetupInProgress = TRUE;
741         SacFreePool(PartialInfo);
742         if (SetupInProgress)
743         {
744             /* Yes, so we'll use a special hostname to identify this */
745             MessageBuffer = GetMessage(SAC_UNINITIALIZED_MSG);
746             Size = wcslen(MessageBuffer);
747             ASSERT(Size > 0);
748             RealSize = Size * sizeof(WCHAR) + sizeof(UNICODE_NULL);
749 
750             /* Make room for it and copy it in there */
751             MachineInformation->MachineName = SacAllocatePool(RealSize,
752                                                               GLOBAL_BLOCK_TAG);
753             if (MachineInformation->MachineName)
754             {
755                 wcscpy(MachineInformation->MachineName, MessageBuffer);
756             }
757         }
758     }
759 
760     /* If we are not in setup mode, or if we failed to check... */
761     if (!SetupInProgress)
762     {
763         /* Query the computer name */
764         Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\"
765                                         L"CurrentControlSet\\Control\\"
766                                         L"ComputerName\\ComputerName",
767                                         L"ComputerName",
768                                         &PartialInfo);
769         if (!NT_SUCCESS(Status))
770         {
771             /* It's not critical, but we won't have it */
772             SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Failed to get machine name.\n");
773         }
774         else
775         {
776             /* We have the name, copy it from the registry */
777             Status = CopyRegistryValueData((PVOID*)&MachineInformation->
778                                            MachineName,
779                                            PartialInfo);
780             SacFreePool(PartialInfo);
781             if (!NT_SUCCESS(Status))
782             {
783                 SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (20).\n");
784                 goto Fail;
785             }
786         }
787     }
788 
789     /* Next step, try to get the machine GUID */
790     RtlZeroMemory(&SystemGuid, sizeof(SystemGuid));
791     OutputSize = sizeof(SystemGuid);
792     Status = HeadlessDispatch(HeadlessCmdQueryGUID,
793                               NULL,
794                               0,
795                               &SystemGuid,
796                               &OutputSize);
797     if (!NT_SUCCESS(Status))
798     {
799         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Failed to get Machine GUID.\n");
800     }
801     else
802     {
803         /* We have it -- make room for it */
804         GuidString = SacAllocatePool(0x50, GLOBAL_BLOCK_TAG);
805         if (!GuidString)
806         {
807             SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (31).\n");
808             goto Fail;
809         }
810 
811         /* Build the string with the GUID in it, and save the ppointer to it */
812         swprintf(GuidString,
813                  L"%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
814                  SystemGuid.Data1,
815                  SystemGuid.Data2,
816                  SystemGuid.Data3,
817                  SystemGuid.Data4[0],
818                  SystemGuid.Data4[1],
819                  SystemGuid.Data4[2],
820                  SystemGuid.Data4[3],
821                  SystemGuid.Data4[4],
822                  SystemGuid.Data4[5],
823                  SystemGuid.Data4[6],
824                  SystemGuid.Data4[7]);
825         MachineInformation->MachineGuid = GuidString;
826     }
827 
828     /* Next, query the processor architecture */
829     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\"
830                                     L"CurrentControlSet\\Control\\"
831                                     L"Session Manager\\Environment",
832                                     L"PROCESSOR_ARCHITECTURE",
833                                     &PartialInfo);
834     if (!NT_SUCCESS(Status))
835     {
836         /* It's not critical, but we won't have it */
837         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (30).\n");
838     }
839     else
840     {
841         /* We have it! Copy the value from the registry */
842         Status = CopyRegistryValueData((PVOID*)&MachineInformation->
843                                        ProcessorArchitecture,
844                                        PartialInfo);
845         SacFreePool(PartialInfo);
846         if (!NT_SUCCESS(Status))
847         {
848             SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (30).\n");
849             goto Fail;
850         }
851     }
852 
853     /* Now allocate a buffer for the OS version number */
854     MajorVersion = SacAllocatePool(0x18, GLOBAL_BLOCK_TAG);
855     if (!MajorVersion)
856     {
857         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (50).\n");
858         goto Fail;
859     }
860 
861     /* Build the buffer and set a pointer to it */
862     swprintf(MajorVersion,
863              L"%d.%d",
864              VersionInformation.dwMajorVersion,
865              VersionInformation.dwMinorVersion);
866     MachineInformation->MajorVersion = MajorVersion;
867 
868     /* Now allocate a buffer for the OS build number */
869     BuildNumber = SacAllocatePool(0xC, GLOBAL_BLOCK_TAG);
870     if (!BuildNumber)
871     {
872         SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC InitializeMachineInformation: Exiting (60).\n");
873         goto Fail;
874     }
875 
876     /* Build the buffer and set a pointer to it */
877     swprintf(BuildNumber, L"%d", VersionInformation.dwBuildNumber);
878     MachineInformation->BuildNumber = BuildNumber;
879 
880     /* Now check what kind of SKU this is */
881     if (ExVerifySuite(DataCenter))
882     {
883         SuiteTypeMessage = SAC_DATACENTER_SUITE_MSG;
884     }
885     else if (ExVerifySuite(EmbeddedNT))
886     {
887         SuiteTypeMessage = SAC_EMBEDDED_SUITE_MSG;
888     }
889     else if (ExVerifySuite(Enterprise))
890     {
891         SuiteTypeMessage = SAC_ENTERPRISE_SUITE_MSG;
892     }
893     else
894     {
895         /* Unknown or perhaps a client SKU */
896         SuiteTypeMessage = SAC_NO_SUITE_MSG;
897     }
898 
899     /* Get the string that corresponds to the SKU type */
900     MessageBuffer = GetMessage(SuiteTypeMessage);
901     if (!MessageBuffer)
902     {
903         /* We won't have it, but this isn't critical */
904         SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed to get product type.\n");
905     }
906     else
907     {
908         /* Calculate the size we need to hold the string */
909         Size = wcslen(MessageBuffer);
910         ASSERT(Size > 0);
911         RealSize = Size * sizeof(WCHAR) + sizeof(UNICODE_NULL);
912 
913         /* Allocate a buffer for it */
914         ProductType = SacAllocatePool(RealSize, GLOBAL_BLOCK_TAG);
915         if (!ProductType)
916         {
917             SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed product type memory allocation.\n");
918             goto Fail;
919         }
920 
921         /* Copy the string and set the pointer */
922         RtlCopyMemory(ProductType, MessageBuffer, RealSize);
923         MachineInformation->ProductType = ProductType;
924     }
925 
926     /* Check if this is a SP version or RTM version */
927     if (VersionInformation.wServicePackMajor)
928     {
929         /* This is a service pack, allocate a buffer for the version */
930         ServicePack = SacAllocatePool(0x18, GLOBAL_BLOCK_TAG);
931         if (ServicePack)
932         {
933             /* Build the buffer and set a pointer to it */
934             swprintf(ServicePack,
935                      L"%d.%d",
936                      VersionInformation.wServicePackMajor,
937                      VersionInformation.wServicePackMinor);
938             MachineInformation->ServicePack = ServicePack;
939 
940             /* We've collected all the machine info and are done! */
941             return;
942         }
943 
944         /* This is the failure path */
945         SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed service pack memory allocation.\n");
946     }
947     else
948     {
949         /* Get a generic string that indicates there's no service pack */
950         MessageBuffer = GetMessage(SAC_NO_DATA_MSG);
951         Size = wcslen(MessageBuffer);
952         ASSERT(Size > 0);
953         RealSize = Size * sizeof(WCHAR) + sizeof(UNICODE_NULL);
954 
955         /* Allocate memory for the "no service pack" string */
956         ServicePack = SacAllocatePool(RealSize, GLOBAL_BLOCK_TAG);
957         if (ServicePack)
958         {
959             /* Copy the buffer and set a pointer to it */
960             RtlCopyMemory(ServicePack, MessageBuffer, RealSize);
961             MachineInformation->ServicePack = ServicePack;
962 
963             /* We've collected all the machine info and are done! */
964             return;
965         }
966 
967         SAC_DBG(SAC_DBG_INIT, "SAC InitializeMachineInformation: Failed service pack memory allocation.\n");
968     }
969 
970 Fail:
971     /* In the failure path, always cleanup the machine information buffer */
972     if (MachineInformation)
973     {
974         SacFreePool(MachineInformation);
975     }
976     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC Initialize Machine Information : Exiting with error.\n");
977 }
978 
979 NTSTATUS
980 NTAPI
981 GetCommandConsoleLaunchingPermission(OUT PBOOLEAN Permission)
982 {
983     NTSTATUS Status;
984     PKEY_VALUE_PARTIAL_INFORMATION Dummy;
985 
986     /* Assume success and read the key */
987     *Permission = TRUE;
988     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacdrv",
989                                     L"DisableCmdSessions",
990                                     &Dummy);
991     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
992     {
993         /* The default is success */
994         Status = STATUS_SUCCESS;
995     }
996     else
997     {
998         /* Only if the key is present and set, do we disable permission */
999         if (NT_SUCCESS(Status)) *Permission = FALSE;
1000     }
1001 
1002     /* Return status */
1003     return Status;
1004 }
1005 
1006 NTSTATUS
1007 NTAPI
1008 ImposeSacCmdServiceStartTypePolicy(VOID)
1009 {
1010     NTSTATUS Status;
1011     PKEY_VALUE_PARTIAL_INFORMATION Buffer = NULL;
1012     PULONG Data;
1013 
1014     /* Read the service start type*/
1015     Status = GetRegistryValueBuffer(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacsvr",
1016                                     L"Start",
1017                                     &Buffer);
1018     if (!NT_SUCCESS(Status)) return Status;
1019 
1020     /* If there's no start type, fail, as this is unusual */
1021     if (!Buffer) return STATUS_UNSUCCESSFUL;
1022 
1023     /* Read the value */
1024     Status = CopyRegistryValueData((PVOID*)&Data, Buffer);
1025     SacFreePool(Buffer);
1026     if (!NT_SUCCESS(Status)) return Status;
1027 
1028     /* Check what the current start type is */
1029     switch (*Data)
1030     {
1031         /* It's boot, system, or disabled */
1032         case 1:
1033         case 2:
1034         case 4:
1035             /* Leave it as is */
1036             return Status;
1037 
1038         case 3:
1039 
1040             /* It's set to automatic, set it to system instead */
1041             *Data = 2;
1042             Status = SetRegistryValue(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\sacsvr",
1043                                       L"Start",
1044                                       REG_DWORD,
1045                                       Data,
1046                                       sizeof(ULONG));
1047             if (!NT_SUCCESS(Status))
1048             {
1049                 SAC_DBG(SAC_DBG_INIT, "SAC ImposeSacCmdServiceStartTypePolicy: Failed SetRegistryValue: %X\n", Status);
1050             }
1051             break;
1052 
1053         default:
1054             ASSERT(FALSE);
1055     }
1056 
1057     return Status;
1058 }
1059 
1060 VOID
1061 NTAPI
1062 InitializeCmdEventInfo(VOID)
1063 {
1064     /* Check if we were already initialized */
1065     if (HaveUserModeServiceCmdEventInfo)
1066     {
1067         /* Full state expected */
1068         ASSERT(RequestSacCmdEventObjectBody);
1069         ASSERT(RequestSacCmdSuccessEventObjectBody);
1070         ASSERT(RequestSacCmdFailureEventObjectBody);
1071 
1072         /* Dereference each wait object in turn */
1073         if (RequestSacCmdEventObjectBody)
1074         {
1075             ObDereferenceObject(RequestSacCmdEventObjectBody);
1076         }
1077 
1078         if (RequestSacCmdSuccessEventObjectBody)
1079         {
1080             ObDereferenceObject(RequestSacCmdSuccessEventObjectBody);
1081         }
1082 
1083         if (RequestSacCmdFailureEventObjectBody)
1084         {
1085             ObDereferenceObject(RequestSacCmdFailureEventObjectBody);
1086         }
1087     }
1088 
1089     /* Claer everything */
1090     RequestSacCmdEventObjectBody = NULL;
1091     RequestSacCmdEventWaitObjectBody = NULL;
1092     RequestSacCmdSuccessEventObjectBody = NULL;
1093     RequestSacCmdSuccessEventWaitObjectBody = NULL;
1094     RequestSacCmdFailureEventObjectBody = NULL;
1095     RequestSacCmdFailureEventWaitObjectBody = NULL;
1096     ServiceProcessFileObject = NULL;
1097 
1098     /* Reset state */
1099     HaveUserModeServiceCmdEventInfo = FALSE;
1100 }
1101 
1102 NTSTATUS
1103 NTAPI
1104 RegisterBlueScreenMachineInformation(VOID)
1105 {
1106     PWCHAR XmlBuffer;
1107     PHEADLESS_CMD_SET_BLUE_SCREEN_DATA BsBuffer;
1108     SIZE_T Length, HeaderLength, TotalLength;
1109     NTSTATUS Status;
1110     ULONG i;
1111 
1112     /* Create the XML buffer and make sure it's OK */
1113     Status = TranslateMachineInformationXML(&XmlBuffer, NULL);
1114     CHECK_PARAMETER_WITH_STATUS(NT_SUCCESS(Status), Status);
1115     CHECK_PARAMETER_WITH_STATUS(XmlBuffer, STATUS_UNSUCCESSFUL);
1116 
1117     /* Compute the sizes and allocate a buffer for it */
1118     Length = wcslen(XmlBuffer);
1119     HeaderLength = strlen("MACHINEINFO");
1120     TotalLength = HeaderLength +
1121                   Length +
1122                   sizeof(*BsBuffer) +
1123                   2 * sizeof(ANSI_NULL);
1124     BsBuffer = SacAllocatePool(TotalLength, GLOBAL_BLOCK_TAG);
1125     CHECK_PARAMETER_WITH_STATUS(BsBuffer, STATUS_NO_MEMORY);
1126 
1127     /* Copy the XML property name */
1128     strcpy((PCHAR)BsBuffer->Data, "MACHINEINFO");
1129     BsBuffer->ValueIndex = HeaderLength + sizeof(ANSI_NULL);
1130 
1131     /* Copy the data and NULL-terminate it */
1132     for (i = 0; i < Length; i++)
1133     {
1134         BsBuffer->Data[BsBuffer->ValueIndex + i] = XmlBuffer[i];
1135     }
1136     BsBuffer->Data[BsBuffer->ValueIndex + i] = ANSI_NULL;
1137 
1138     /* Let the OS save the buffer for later */
1139     Status = HeadlessDispatch(HeadlessCmdSetBlueScreenData,
1140                               BsBuffer,
1141                               TotalLength,
1142                               NULL,
1143                               NULL);
1144 
1145     /* Failure or not, we don't need this anymore */
1146     SacFreePool(BsBuffer);
1147     SacFreePool(XmlBuffer);
1148 
1149     /* Return the result */
1150     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC Initialize Machine Information: Exiting.\n");
1151     return Status;
1152 }
1153 
1154 VOID
1155 NTAPI
1156 FreeMachineInformation(VOID)
1157 {
1158     ASSERT(MachineInformation);
1159 
1160     /* Free every cached string of machine information */
1161     if (MachineInformation->MachineName) SacFreePool(MachineInformation);
1162     if (MachineInformation->MachineGuid) SacFreePool(MachineInformation->MachineGuid);
1163     if (MachineInformation->ProcessorArchitecture) SacFreePool(MachineInformation->ProcessorArchitecture);
1164     if (MachineInformation->MajorVersion) SacFreePool(MachineInformation->MajorVersion);
1165     if (MachineInformation->BuildNumber) SacFreePool(MachineInformation->BuildNumber);
1166     if (MachineInformation->ProductType) SacFreePool(MachineInformation->ProductType);
1167     if (MachineInformation->ServicePack) SacFreePool(MachineInformation->ServicePack);
1168 }
1169 
1170 BOOLEAN
1171 NTAPI
1172 VerifyEventWaitable(IN HANDLE Handle,
1173                     OUT PVOID *WaitObject,
1174                     OUT PVOID *ActualWaitObject)
1175 {
1176     PVOID Object;
1177     NTSTATUS Status;
1178     POBJECT_TYPE ObjectType;
1179 
1180     /* Reference the object */
1181     Status = ObReferenceObjectByHandle(Handle,
1182                                        EVENT_ALL_ACCESS,
1183                                        NULL,
1184                                        KernelMode,
1185                                        &Object,
1186                                        NULL);
1187     *WaitObject = Object;
1188     if (!NT_SUCCESS(Status))
1189     {
1190         SAC_DBG(SAC_DBG_INIT, "SAC VerifyEventWaitable: Unable to reference event object (%lx)\n", Status);
1191         return FALSE;
1192     }
1193 
1194     /* Check if the object itself is NOT being used */
1195     ObjectType = OBJECT_TO_OBJECT_HEADER(Object)->Type;
1196     if (ObjectType->TypeInfo.UseDefaultObject == FALSE)
1197     {
1198         /* Get the actual object that's being used for the wait */
1199         *ActualWaitObject = (PVOID)((ULONG_PTR)Object +
1200                                     (ULONG_PTR)ObjectType->DefaultObject);
1201         return TRUE;
1202     }
1203 
1204     /* Drop the reference we took */
1205     SAC_DBG(SAC_DBG_INIT, "SAC VerifyEventWaitable: event object not waitable!\n");
1206     ObDereferenceObject(*WaitObject);
1207     return FALSE;
1208 }
1209 
1210 NTSTATUS
1211 NTAPI
1212 SerialBufferGetChar(OUT PCHAR Char)
1213 {
1214     /* Check if nothing's been produced yet */
1215     if (SerialPortConsumerIndex == SerialPortProducerIndex)
1216     {
1217         return STATUS_NO_DATA_DETECTED;
1218     }
1219 
1220     /* Consume the produced character and clear it*/
1221     *Char = SerialPortBuffer[SerialPortConsumerIndex];
1222     SerialPortBuffer[SerialPortConsumerIndex] = ANSI_NULL;
1223 
1224     /* Advance the index and return success */
1225     _InterlockedExchange(&SerialPortConsumerIndex,
1226                          (SerialPortConsumerIndex + 1) &
1227                          (SAC_SERIAL_PORT_BUFFER_SIZE - 1));
1228     return STATUS_SUCCESS;
1229 }
1230 
1231 ULONG
1232 NTAPI
1233 GetMessageLineCount(IN ULONG MessageIndex)
1234 {
1235     ULONG LineCount = 0;
1236     PWCHAR Buffer;
1237 
1238     /* Get the message buffer */
1239     Buffer = GetMessage(MessageIndex);
1240     if (Buffer)
1241     {
1242         /* Scan it looking for new lines, and increment the count each time */
1243         while (*Buffer) if (*Buffer++ == L'\n') ++LineCount;
1244     }
1245 
1246     /* Return the line count */
1247     return LineCount;
1248 }
1249 
1250 ULONG
1251 ConvertAnsiToUnicode(
1252     IN PWCHAR pwch,
1253     IN PCHAR pch,
1254     IN ULONG length
1255     )
1256 {
1257     return STATUS_NOT_IMPLEMENTED;
1258 }
1259 
1260 BOOLEAN
1261 IsCmdEventRegistrationProcess(
1262     IN PFILE_OBJECT FileObject
1263     )
1264 {
1265     return FALSE;
1266 }
1267 
1268 NTSTATUS
1269 InvokeUserModeService(
1270     VOID
1271     )
1272 {
1273     return STATUS_NOT_IMPLEMENTED;
1274 }
1275 
1276 NTSTATUS
1277 TranslateMachineInformationText(
1278     IN PWCHAR Buffer)
1279 {
1280     return STATUS_NOT_IMPLEMENTED;
1281 }
1282 
1283 NTSTATUS
1284 CopyAndInsertStringAtInterval(
1285     IN PWCHAR SourceStr,
1286     IN ULONG Interval,
1287     IN PWCHAR InsertStr,
1288     OUT PWCHAR pDestStr
1289     )
1290 {
1291     return STATUS_NOT_IMPLEMENTED;
1292 }
1293 
1294 NTSTATUS
1295 RegisterSacCmdEvent(
1296     IN PVOID Object,
1297     IN PKEVENT SetupCmdEvent[]
1298     )
1299 {
1300     return STATUS_NOT_IMPLEMENTED;
1301 }
1302 
1303 NTSTATUS
1304 UnregisterSacCmdEvent(
1305     IN PFILE_OBJECT FileObject
1306     )
1307 {
1308     return STATUS_NOT_IMPLEMENTED;
1309 }
1310