1 /*
2  * PROJECT:     ReactOS Whoami
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/applications/cmdutils/whoami/whoami.c
5  * PURPOSE:     Displays information about the current local user, groups and privileges.
6  * PROGRAMMERS: Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
7  */
8 
9 #define SECURITY_WIN32
10 #include <security.h>
11 #include <sddl.h>
12 #include <strsafe.h>
13 
14 #include <conutils.h>
15 
16 #include "resource.h"
17 
18 #define wprintf(...) ConPrintf(StdOut, ##__VA_ARGS__)
19 
20 BOOL NoHeader = FALSE;
21 UINT NoHeaderArgCount = 0;
22 UINT PrintFormatArgCount = 0;
23 
24 enum
25 {
26     undefined,
27     table,
28     list,
29     csv
30 } PrintFormat = undefined;
31 
32 
33 BOOL GetArgument(WCHAR* arg, int argc, WCHAR* argv[])
34 {
35     int i;
36 
37     if (!arg)
38         return FALSE;
39 
40     for (i = 1; i < argc; i++)
41     {
42         if (wcsicmp(argv[i], arg) == 0)
43             return TRUE;
44     }
45 
46     return FALSE;
47 }
48 
49 /* blanking out the accepted modifiers will make filtering easier later on */
50 void BlankArgument(int argc, WCHAR* argv[])
51 {
52     argv[argc] = L"";
53 }
54 
55 /* helper functions; let's keep it tidy to avoid redundancies */
56 
57 LPWSTR WhoamiGetUser(EXTENDED_NAME_FORMAT NameFormat)
58 {
59     LPWSTR UsrBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
60     ULONG UsrSiz = MAX_PATH;
61 
62     if (UsrBuf == NULL)
63         return NULL;
64 
65     if (GetUserNameExW(NameFormat, UsrBuf, &UsrSiz))
66     {
67         CharLowerW(UsrBuf);
68         return UsrBuf;
69     }
70 
71     HeapFree(GetProcessHeap(), 0, UsrBuf);
72     return NULL;
73 }
74 
75 BOOL WhoamiFree(VOID* Buffer)
76 {
77     return HeapFree(GetProcessHeap(), 0, Buffer);
78 }
79 
80 
81 VOID* WhoamiGetTokenInfo(TOKEN_INFORMATION_CLASS TokenType)
82 {
83     HANDLE hToken = 0;
84     DWORD dwLength = 0;
85     VOID* pTokenInfo = 0;
86 
87     if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
88     {
89         GetTokenInformation(hToken,
90                             TokenType,
91                             NULL,
92                             dwLength,
93                             &dwLength);
94 
95         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
96         {
97             pTokenInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
98             if (pTokenInfo == NULL)
99             {
100                 wprintf(L"ERROR: not enough memory to allocate the token structure.\r\n");
101                 exit(1);
102             }
103         }
104 
105         if (!GetTokenInformation(hToken, TokenType,
106                                  (LPVOID)pTokenInfo,
107                                  dwLength,
108                                  &dwLength))
109         {
110             wprintf(L"ERROR 0x%x: could not get token information.\r\n", GetLastError());
111             WhoamiFree(pTokenInfo);
112             exit(1);
113         }
114 
115         CloseHandle(hToken);
116     }
117 
118     return pTokenInfo;
119 }
120 
121 LPWSTR WhoamiLoadRcString(INT ResId)
122 {
123     #define RC_STRING_MAX_SIZE 850
124     static WCHAR TmpBuffer[RC_STRING_MAX_SIZE];
125 
126     LoadStringW(GetModuleHandleW(NULL), ResId, TmpBuffer, RC_STRING_MAX_SIZE);
127 
128     return TmpBuffer;
129 }
130 
131 void WhoamiPrintHeader(int HeaderId)
132 {
133     PWSTR Header = WhoamiLoadRcString(HeaderId);
134     DWORD Length = wcslen(Header);
135 
136     if (NoHeader || PrintFormat == csv)
137         return;
138 
139     wprintf(L"\n%s\n", Header);
140 
141     while (Length--)
142         wprintf(L"-");
143 
144     wprintf(L"\n\n");
145 }
146 
147 typedef struct
148 {
149     UINT Rows;
150     UINT Cols;
151     LPWSTR Content[1];
152 } WhoamiTable;
153 
154 /* create and prepare a new table for printing */
155 WhoamiTable *WhoamiAllocTable(UINT Rows, UINT Cols)
156 {
157     WhoamiTable *pTable = HeapAlloc(GetProcessHeap(),
158                                     HEAP_ZERO_MEMORY,
159                                     sizeof(WhoamiTable) + sizeof(LPWSTR) * Rows * Cols);
160 
161     // wprintf(L"DEBUG: Allocating %dx%d elem table for printing.\r\n\r\n", Rows, Cols);
162 
163     if (!pTable)
164     {
165         wprintf(L"ERROR: Not enough memory for displaying the table.");
166         exit(1);
167     }
168 
169     pTable->Rows = Rows;
170     pTable->Cols = Cols;
171 
172     return pTable;
173 }
174 
175 /* allocate and fill a new entry in the table */
176 void WhoamiSetTable(WhoamiTable *pTable, WCHAR *Entry, UINT Row, UINT Col)
177 {
178     LPWSTR Target = HeapAlloc(GetProcessHeap(),
179                               HEAP_ZERO_MEMORY,
180                               1 + wcslen(Entry) * sizeof(Entry[0]));
181 
182     // wprintf(L"DEBUG: Setting table value '%lp' '%ls' for %lu %lu.\n", entry, entry, row, col);
183 
184     if (!Target)
185         exit(1);
186 
187     wcscpy(Target, Entry);
188 
189     pTable->Content[Row * pTable->Cols + Col] = Target;
190 }
191 
192 /* fill a new entry in the table */
193 void WhoamiSetTableDyn(WhoamiTable *pTable, WCHAR *Entry, UINT Row, UINT Col)
194 {
195     pTable->Content[Row * pTable->Cols + Col] = Entry;
196 }
197 
198 /* print and deallocate the table */
199 void WhoamiPrintTable(WhoamiTable *pTable)
200 {
201     UINT i, j;
202     UINT CurRow, CurCol;
203     UINT *ColLength;
204 
205 
206     if (!pTable)
207     {
208         wprintf(L"ERROR: The table passed for display is empty.");
209         exit(1);
210     }
211 
212     /* if we are going to print a *list* or *table*; take note of the total
213        column size, as we will need it later on when printing them in a tabular
214        fashion, according to their windows counterparts */
215 
216     if (PrintFormat != csv)
217     {
218         ColLength = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(UINT) * pTable->Cols);
219 
220         if (PrintFormat == list)
221         {
222             for (j = 0; j < pTable->Cols; j++)
223                 if (pTable->Content[j])
224                 {
225                     UINT ThisLength = wcslen(pTable->Content[j]);
226 
227                     /* now that we're here, seize the opportunity and add those pesky ":" */
228                     pTable->Content[j][ThisLength++] = L':';
229                     pTable->Content[j][ThisLength] = UNICODE_NULL;
230 
231                     ColLength[0] = max(ThisLength, ColLength[0]);
232                 }
233         }
234         else
235         {
236             for (j = 0; j < pTable->Cols; j++)
237                 for (i = 0; i < pTable->Rows; i++)
238                     if (pTable->Content[i * pTable->Cols + j])
239                     {
240                         UINT ThisLength = wcslen(pTable->Content[i * pTable->Cols + j]);
241                         ColLength[j] = max(ThisLength, ColLength[j]);
242                     }
243         }
244     }
245 
246     switch (PrintFormat)
247     {
248         case csv:
249         {
250             for (i = 0; i < pTable->Rows; i++)
251             {
252                 if (!pTable->Content[i * pTable->Cols])
253                     continue;
254 
255                 /* if the user especified /nh then skip the column labels */
256                 if (NoHeader && i == 0)
257                     continue;
258 
259                 for (j = 0; j < pTable->Cols; j++)
260                 {
261                     if (pTable->Content[i * pTable->Cols + j])
262                     {
263                         wprintf(L"\"%s\"%s",
264                                 pTable->Content[i * pTable->Cols + j],
265                                 (j+1 < pTable->Cols ? L"," : L""));
266                     }
267                 }
268                 wprintf(L"\n");
269             }
270 
271             break;
272 
273         }
274 
275         case list:
276         {
277             UINT FinalRow = 0;
278 
279             /* fixme: we need to do two passes to find out which entry is the last one shown, or not null this is not exactly optimal */
280             for (CurRow = 1; CurRow < pTable->Rows; CurRow++)
281             {
282                 /* if the first member of this row isn't available, then forget it */
283                 if (!pTable->Content[CurRow * pTable->Cols])
284                     continue;
285 
286                 FinalRow = CurRow;
287             }
288 
289             for (CurRow = 1; CurRow < pTable->Rows; CurRow++)
290             {
291                 /* if the first member of this row isn't available, then forget it */
292                 if (!pTable->Content[CurRow * pTable->Cols])
293                     continue;
294 
295                 /* if the user especified /nh then skip the column labels */
296                 if (NoHeader && i == 0)
297                     continue;
298 
299                 for (CurCol = 0; CurCol < pTable->Cols; CurCol++)
300                 {
301                     wprintf(L"%-*s %s\n",
302                             ColLength[0],
303                             pTable->Content[CurCol],
304                             pTable->Content[CurRow * pTable->Cols + CurCol]);
305                 }
306 
307                 /* don't add two carriage returns at the very end */
308                 if (CurRow != FinalRow)
309                     wprintf(L"\n");
310             }
311 
312             break;
313         }
314 
315 
316         case table:
317         default:
318         {
319             for (i = 0; i < pTable->Rows; i++)
320             {
321                 /* if the first member of this row isn't available, then forget it */
322                 if (!pTable->Content[i * pTable->Cols])
323                     continue;
324 
325                 /* if the user especified /nh then skip the column labels too */
326                 if (NoHeader && i == 0)
327                     continue;
328 
329                 for (j = 0; j < pTable->Cols; j++)
330                 {
331                     if (pTable->Content[i * pTable->Cols + j])
332                     {
333                         wprintf(L"%-*s ", ColLength[j], pTable->Content[i * pTable->Cols + j]);
334                     }
335                 }
336                 wprintf(L"\n");
337 
338                 /* add the cute underline thingie for the table header */
339                 if (i == 0)
340                 {
341                     for (j = 0; j < pTable->Cols; j++)
342                     {
343                         DWORD Length = ColLength[j];
344 
345                         while (Length--)
346                             wprintf(L"=");
347 
348                         /* a spacing between all the columns except for the last one */
349                         if (pTable->Cols != (i + 1))
350                             wprintf(L" ");
351                     }
352 
353                     wprintf(L"\n");
354                 }
355             }
356 
357         }
358     }
359 
360     /* fixme: when many tables are displayed in a single run we
361               have to sandwich carriage returns in between. */
362     // if (!final_entry)
363         wprintf(L"\n");
364 
365     for (i = 0; i < pTable->Rows; i++)
366         for (j = 0; j < pTable->Cols; j++)
367             WhoamiFree(pTable->Content[i * pTable->Cols + j]);
368 
369     WhoamiFree(pTable);
370 
371     if (PrintFormat != csv)
372         HeapFree(GetProcessHeap(), 0, ColLength);
373 }
374 
375 int WhoamiLogonId(void)
376 {
377     PTOKEN_GROUPS pGroupInfo = (PTOKEN_GROUPS) WhoamiGetTokenInfo(TokenGroups);
378     DWORD dwIndex = 0;
379     LPWSTR pSidStr = 0;
380     PSID pSid = 0;
381 
382     if (pGroupInfo == NULL)
383         return 0;
384 
385     /* lets see if we can find the logon SID in that list, should be there */
386     for (dwIndex = 0; dwIndex < pGroupInfo->GroupCount; dwIndex++)
387     {
388         if ((pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
389         {
390             pSid = pGroupInfo->Groups[dwIndex].Sid;
391         }
392     }
393 
394     if (pSid == 0)
395     {
396         WhoamiFree(pGroupInfo);
397         wprintf(L"ERROR: Couldn't find the logon SID.\n");
398         return 1;
399     }
400     if (!ConvertSidToStringSidW(pSid, &pSidStr))
401     {
402         WhoamiFree(pGroupInfo);
403         wprintf(L"ERROR: Couldn't convert the logon SID to a string.\n");
404         return 1;
405     }
406     else
407     {
408         /* let's show our converted logon SID */
409         wprintf(L"%s\n", pSidStr);
410     }
411 
412     /* cleanup our allocations */
413     LocalFree(pSidStr);
414     WhoamiFree(pGroupInfo);
415 
416     return 0;
417 }
418 
419 int WhoamiUser(void)
420 {
421     PTOKEN_USER pUserInfo = (PTOKEN_USER) WhoamiGetTokenInfo(TokenUser);
422     LPWSTR pUserStr = NULL;
423     LPWSTR pSidStr = NULL;
424     WhoamiTable *UserTable = NULL;
425 
426     if (pUserInfo == NULL)
427     {
428         return 1;
429     }
430 
431     pUserStr = WhoamiGetUser(NameSamCompatible);
432     if (pUserStr == NULL)
433     {
434         WhoamiFree(pUserInfo);
435         return 1;
436     }
437 
438     UserTable = WhoamiAllocTable(2, 2);
439 
440     WhoamiPrintHeader(IDS_USER_HEADER);
441 
442     /* set the column labels */
443     WhoamiSetTable(UserTable, WhoamiLoadRcString(IDS_COL_USER_NAME), 0, 0);
444     WhoamiSetTable(UserTable, WhoamiLoadRcString(IDS_COL_SID), 0, 1);
445 
446     ConvertSidToStringSidW(pUserInfo->User.Sid, &pSidStr);
447 
448     /* set the values for our single row of data */
449     WhoamiSetTable(UserTable, pUserStr, 1, 0);
450     WhoamiSetTable(UserTable, pSidStr, 1, 1);
451 
452     WhoamiPrintTable(UserTable);
453 
454     /* cleanup our allocations */
455     LocalFree(pSidStr);
456     WhoamiFree(pUserInfo);
457     WhoamiFree(pUserStr);
458 
459     return 0;
460 }
461 
462 int WhoamiGroups(void)
463 {
464     DWORD dwIndex = 0;
465     LPWSTR pSidStr = 0;
466 
467     static WCHAR szGroupName[255] = {0};
468     static WCHAR szDomainName[255] = {0};
469 
470     DWORD cchGroupName  = _countof(szGroupName);
471     DWORD cchDomainName = _countof(szGroupName);
472 
473     SID_NAME_USE Use = 0;
474     BYTE SidNameUseStr[12] =
475     {
476         /* SidTypeUser           */ -1,
477         /* SidTypeGroup          */ -1,
478         /* SidTypeDomain         */ -1,
479         /* SidTypeUser           */ -1,
480         /* SidTypeAlias          */ IDS_TP_ALIAS,
481         /* SidTypeWellKnownGroup */ IDS_TP_WELL_KNOWN_GROUP,
482         /* SidTypeDeletedAccount */ -1,
483         /* SidTypeInvalid        */ -1,
484         /* SidTypeUnknown        */ -1,
485         /* SidTypeComputer       */ -1,
486         /* SidTypeLabel          */ IDS_TP_LABEL
487     };
488 
489     PTOKEN_GROUPS pGroupInfo = (PTOKEN_GROUPS)WhoamiGetTokenInfo(TokenGroups);
490     UINT PrintingRow;
491     WhoamiTable *GroupTable = NULL;
492 
493     if (pGroupInfo == NULL)
494     {
495         return 1;
496     }
497 
498     /* the header is the first (0) row, so we start in the second one (1) */
499     PrintingRow = 1;
500 
501     GroupTable = WhoamiAllocTable(pGroupInfo->GroupCount + 1, 4);
502 
503     WhoamiPrintHeader(IDS_GROU_HEADER);
504 
505     WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_GROUP_NAME), 0, 0);
506     WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_TYPE), 0, 1);
507     WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_SID), 0, 2);
508     WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_ATTRIB), 0, 3);
509 
510     for (dwIndex = 0; dwIndex < pGroupInfo->GroupCount; dwIndex++)
511     {
512         LookupAccountSidW(NULL,
513                           pGroupInfo->Groups[dwIndex].Sid,
514                           (LPWSTR)&szGroupName,
515                           &cchGroupName,
516                           (LPWSTR)&szDomainName,
517                           &cchDomainName,
518                           &Use);
519 
520         /* the original tool seems to limit the list to these kind of SID items */
521         if ((Use == SidTypeWellKnownGroup || Use == SidTypeAlias ||
522             Use == SidTypeLabel) && !(pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID))
523         {
524                 wchar_t tmpBuffer[666];
525 
526             /* looks like windows treats 0x60 as 0x7 for some reason, let's just nod and call it a day:
527                0x60 is SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED
528                0x07 is SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED */
529 
530             if (pGroupInfo->Groups[dwIndex].Attributes == 0x60)
531                 pGroupInfo->Groups[dwIndex].Attributes = 0x07;
532 
533             /* 1- format it as DOMAIN\GROUP if the domain exists, or just GROUP if not */
534             _snwprintf((LPWSTR)&tmpBuffer,
535                        _countof(tmpBuffer),
536                        L"%s%s%s",
537                        szDomainName,
538                        cchDomainName ? L"\\" : L"",
539                        szGroupName);
540 
541             WhoamiSetTable(GroupTable, tmpBuffer, PrintingRow, 0);
542 
543             /* 2- let's find out the group type by using a simple lookup table for lack of a better method */
544             WhoamiSetTable(GroupTable, WhoamiLoadRcString(SidNameUseStr[Use]), PrintingRow, 1);
545 
546             /* 3- turn that SID into text-form */
547             ConvertSidToStringSidW(pGroupInfo->Groups[dwIndex].Sid, &pSidStr);
548 
549             WhoamiSetTable(GroupTable, pSidStr, PrintingRow, 2);
550 
551             LocalFree(pSidStr);
552 
553             /* 4- reuse that buffer for appending the attributes in text-form at the very end */
554             ZeroMemory(tmpBuffer, sizeof(tmpBuffer));
555 
556             if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_MANDATORY)
557                 StringCchCat(tmpBuffer, _countof(tmpBuffer), WhoamiLoadRcString(IDS_ATTR_GROUP_MANDATORY));
558             if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_ENABLED_BY_DEFAULT)
559                 StringCchCat(tmpBuffer, _countof(tmpBuffer), WhoamiLoadRcString(IDS_ATTR_GROUP_ENABLED_BY_DEFAULT));
560             if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_ENABLED)
561                 StringCchCat(tmpBuffer, _countof(tmpBuffer), WhoamiLoadRcString(IDS_ATTR_GROUP_ENABLED));
562             if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_OWNER)
563                 StringCchCat(tmpBuffer, _countof(tmpBuffer), WhoamiLoadRcString(IDS_ATTR_GROUP_OWNER));
564 
565             /* remove the last comma (', ' which is 2 wchars) of the buffer, let's keep it simple */
566             tmpBuffer[max(wcslen(tmpBuffer) - 2, 0)] = UNICODE_NULL;
567 
568             WhoamiSetTable(GroupTable, tmpBuffer, PrintingRow, 3);
569 
570             PrintingRow++;
571         }
572 
573         /* reset the buffers so that we can reuse them */
574         ZeroMemory(szGroupName, sizeof(szGroupName));
575         ZeroMemory(szDomainName, sizeof(szDomainName));
576 
577         cchGroupName = 255;
578         cchDomainName = 255;
579     }
580 
581     WhoamiPrintTable(GroupTable);
582 
583     /* cleanup our allocations */
584     WhoamiFree((LPVOID)pGroupInfo);
585 
586     return 0;
587 }
588 
589 int WhoamiPriv(void)
590 {
591     PTOKEN_PRIVILEGES pPrivInfo = (PTOKEN_PRIVILEGES) WhoamiGetTokenInfo(TokenPrivileges);
592     DWORD dwResult = 0, dwIndex = 0;
593     WhoamiTable *PrivTable = NULL;
594 
595     if (pPrivInfo == NULL)
596     {
597         return 1;
598     }
599 
600     PrivTable = WhoamiAllocTable(pPrivInfo->PrivilegeCount + 1, 3);
601 
602     WhoamiPrintHeader(IDS_PRIV_HEADER);
603 
604     WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_COL_PRIV_NAME), 0, 0);
605     WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_COL_DESCRIPTION), 0, 1);
606     WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_COL_STATE), 0, 2);
607 
608     for (dwIndex = 0; dwIndex < pPrivInfo->PrivilegeCount; dwIndex++)
609     {
610         PWSTR PrivName = NULL, DispName = NULL;
611         DWORD PrivNameSize = 0, DispNameSize = 0;
612         BOOL ret = FALSE;
613 
614         ret = LookupPrivilegeNameW(NULL,
615                                    &pPrivInfo->Privileges[dwIndex].Luid,
616                                    NULL,
617                                    &PrivNameSize);
618 
619         PrivName = HeapAlloc(GetProcessHeap(), 0, ++PrivNameSize*sizeof(WCHAR));
620 
621         LookupPrivilegeNameW(NULL,
622                              &pPrivInfo->Privileges[dwIndex].Luid,
623                              PrivName,
624                              &PrivNameSize);
625 
626         WhoamiSetTableDyn(PrivTable, PrivName, dwIndex + 1, 0);
627 
628 
629         /* try to grab the size of the string, also, beware, as this call is
630            unimplemented in ReactOS/Wine at the moment */
631 
632         LookupPrivilegeDisplayNameW(NULL, PrivName, NULL, &DispNameSize, &dwResult);
633 
634         DispName = HeapAlloc(GetProcessHeap(), 0, ++DispNameSize * sizeof(WCHAR));
635 
636         ret = LookupPrivilegeDisplayNameW(NULL, PrivName, DispName, &DispNameSize, &dwResult);
637 
638         if (ret && DispName)
639         {
640             // wprintf(L"DispName: %d %x '%s'\n", DispNameSize, GetLastError(), DispName);
641             WhoamiSetTableDyn(PrivTable, DispName, dwIndex + 1, 1);
642         }
643         else
644         {
645             WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_UNKNOWN_DESCRIPTION), dwIndex + 1, 1);
646 
647             if (DispName != NULL)
648                 WhoamiFree(DispName);
649         }
650 
651         if (pPrivInfo->Privileges[dwIndex].Attributes & SE_PRIVILEGE_ENABLED)
652             WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_STATE_ENABLED),  dwIndex + 1, 2);
653         else
654             WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_STATE_DISABLED), dwIndex + 1, 2);
655     }
656 
657     WhoamiPrintTable(PrivTable);
658 
659     /* cleanup our allocations */
660     WhoamiFree(pPrivInfo);
661 
662     return 0;
663 }
664 
665 int wmain(int argc, WCHAR* argv[])
666 {
667     #define WAM_USER   1<<0
668     #define WAM_GROUPS 1<<1
669     #define WAM_PRIV   1<<2
670 
671     INT i;
672     BYTE WamBit = 0;
673 
674     /* Initialize the Console Standard Streams */
675     ConInitStdStreams();
676 
677 
678     /* * * * * * * * * * * * * * * *
679      * A: no parameters whatsoever */
680 
681     if (argc == 1)
682     {
683         /* if there's no arguments just choose the simple path and display the user's identity in lowercase */
684         LPWSTR UserBuffer = WhoamiGetUser(NameSamCompatible);
685 
686         if (UserBuffer)
687         {
688             wprintf(L"%s\n", UserBuffer);
689             WhoamiFree(UserBuffer);
690             return 0;
691         }
692         else
693         {
694             return 1;
695         }
696     }
697 
698     /* first things first-- let's detect and manage both printing modifiers (/fo and /nh) */
699     for (i = 1; i < argc; i++)
700     {
701         if (wcsicmp(argv[i], L"/nh") == 0)
702         {
703             NoHeaderArgCount++;
704 
705             if (NoHeader == FALSE)
706             {
707                 NoHeader = TRUE;
708                 // wprintf(L"Headers disabled!\n");
709                 BlankArgument(i, argv);
710             }
711         }
712     }
713 
714     for (i = 1; i < argc; i++)
715     {
716         if (wcsicmp(argv[i], L"/fo") == 0)
717         {
718             if ((i + 1) < argc)
719             {
720                 // wprintf(L"exists another param after /fo\n");
721 
722                 PrintFormatArgCount++;
723 
724                 if (wcsicmp(argv[i + 1], L"table") == 0 && PrintFormat != table)
725                 {
726                     PrintFormat = table;
727                     // wprintf(L"Changed to table format\n");
728                     BlankArgument(i, argv);
729                     BlankArgument(i + 1, argv);
730                 }
731                 else if (wcsicmp(argv[i + 1], L"list") == 0 && PrintFormat != list)
732                 {
733                     PrintFormat = list;
734                     // wprintf(L"Changed to list format\n");
735                     BlankArgument(i, argv);
736                     BlankArgument(i + 1, argv);
737 
738                     /* looks like you can't use the "/fo list /nh" options together
739                        for some stupid reason */
740                     if (PrintFormat == list && NoHeader != FALSE)
741                     {
742                         wprintf(WhoamiLoadRcString(IDS_ERROR_NH_LIST));
743                         return 1;
744                     }
745                 }
746                 else if (wcsicmp(argv[i + 1], L"csv") == 0 && PrintFormat != csv)
747                 {
748                     PrintFormat = csv;
749                     // wprintf(L"Changed to csv format\n");
750                     BlankArgument(i, argv);
751                     BlankArgument(i + 1, argv);
752                 }
753                 /* /nh or /fo after /fo isn't parsed as a value */
754                 else if (wcsicmp(argv[i + 1], L"/nh") == 0 || wcsicmp(argv[i + 1], L"/fo") == 0
755 
756                 /* same goes for the other named options, not ideal, but works */
757                          || wcsicmp(argv[i + 1], L"/priv") == 0
758                          || wcsicmp(argv[i + 1], L"/groups") == 0
759                          || wcsicmp(argv[i + 1], L"/user") == 0
760                          || wcsicmp(argv[i + 1], L"/all") == 0
761                          || wcsicmp(argv[i + 1], L"") == 0)
762                 {
763                     goto FoValueExpected;
764                 }
765                 else
766                 {
767                     wprintf(WhoamiLoadRcString(IDS_ERROR_VALUENOTALLOWED), argv[i + 1]);
768                     return 1;
769                 }
770             }
771             else
772             {
773                 FoValueExpected:
774 
775                 wprintf(WhoamiLoadRcString(IDS_ERROR_VALUEXPECTED));
776                 return 1;
777             }
778         }
779     }
780 
781     if (NoHeaderArgCount >= 2)
782     {
783         wprintf(WhoamiLoadRcString(IDS_ERROR_1TIMES), L"/nh");
784         return 1;
785     }
786     /* special case when there's just a /nh as argument; it outputs nothing */
787     else if (NoHeaderArgCount == 1 && argc == 2)
788     {
789         return 0;
790     }
791 
792     if (PrintFormatArgCount >= 2)
793     {
794         wprintf(WhoamiLoadRcString(IDS_ERROR_1TIMES), L"/fo");
795         return 1;
796     }
797     /* if there's just /fo <format>... call it invalid */
798     else if (PrintFormatArgCount == 1 && argc == 3)
799     {
800         goto InvalidSyntax;
801     }
802 
803     /* * * * * * * * * * * * * *
804      * B: one single parameter */
805 
806     if (argc == 2)
807     {
808         /* now let's try to parse the triumvirate of simpler, single (1) arguments... plus help */
809         if (wcsicmp(argv[1], L"/?") == 0)
810         {
811             wprintf(WhoamiLoadRcString(IDS_HELP));
812             return 0;
813         }
814 
815         else if (wcsicmp(argv[1], L"/upn") == 0)
816         {
817             LPWSTR UserBuffer = WhoamiGetUser(NameUserPrincipal);
818 
819             if (UserBuffer)
820             {
821                 wprintf(L"%s\n", UserBuffer);
822                 WhoamiFree(UserBuffer);
823                 return 0;
824             }
825             else
826             {
827                 wprintf(WhoamiLoadRcString(IDS_ERROR_UPN));
828                 return 1;
829             }
830         }
831 
832         else if (wcsicmp(argv[1], L"/fqdn") == 0)
833         {
834             LPWSTR UserBuffer = WhoamiGetUser(NameFullyQualifiedDN);
835 
836             if (UserBuffer)
837             {
838                 wprintf(L"%s\n", UserBuffer);
839                 WhoamiFree(UserBuffer);
840                 return 0;
841             }
842             else
843             {
844                 wprintf(WhoamiLoadRcString(IDS_ERROR_FQDN));
845                 return 1;
846             }
847         }
848 
849         else if (wcsicmp(argv[1], L"/logonid") == 0)
850         {
851             return WhoamiLogonId();
852         }
853     }
854 
855     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
856      * C: One main parameter with extra tasty modifiers to play with */
857 
858     /* sometimes is just easier to whitelist for lack of a better method */
859     for (i=1; i<argc; i++)
860     {
861         if ((wcsicmp(argv[i], L"/user") != 0) &&
862             (wcsicmp(argv[i], L"/groups") != 0) &&
863             (wcsicmp(argv[i], L"/priv") != 0) &&
864             (wcsicmp(argv[i], L"/all") != 0) &&
865             (wcsicmp(argv[i], L"") != 0))
866         {
867             wprintf(WhoamiLoadRcString(IDS_ERROR_INVALIDARG), argv[i]);
868             return 1;
869         }
870     }
871 
872     if (GetArgument(L"/user", argc, argv))
873     {
874         WamBit |= WAM_USER;
875     }
876 
877     if (GetArgument(L"/groups", argc, argv))
878     {
879         WamBit |= WAM_GROUPS;
880     }
881 
882     if (GetArgument(L"/priv", argc, argv))
883     {
884         WamBit |= WAM_PRIV;
885     }
886 
887     if (GetArgument(L"/all", argc, argv))
888     {
889         /* one can't have it /all and any of the other options at the same time */
890         if ((WamBit & (WAM_USER | WAM_GROUPS | WAM_PRIV)) == 0)
891         {
892             WamBit |= (WAM_USER | WAM_GROUPS | WAM_PRIV);
893         }
894         else
895         {
896             goto InvalidSyntax;
897         }
898     }
899 
900     if (WamBit & WAM_USER)
901     {
902         WhoamiUser();
903     }
904     if (WamBit & WAM_GROUPS)
905     {
906         WhoamiGroups();
907     }
908     if (WamBit & WAM_PRIV)
909     {
910         WhoamiPriv();
911     }
912 
913     return 0;
914 
915 InvalidSyntax:
916     wprintf(WhoamiLoadRcString(IDS_ERROR_INVALIDSYNTAX));
917     return 1;
918 }
919