From 4d592a5c29db7bdfed231a53f0c4957dbc50e188 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sun, 21 Dec 2014 23:10:46 +0000 Subject: [PATCH] [WHOAMI] Add preliminary version of whoami utility by Ismael Ferreras Morezuelas (with some changes by me). CORE-8533 svn path=/trunk/; revision=65787 --- .../base/applications/cmdutils/CMakeLists.txt | 1 + .../cmdutils/whoami/CMakeLists.txt | 5 + .../cmdutils/whoami/lang/en-US.rc | 56 ++ .../cmdutils/whoami/lang/es-ES.rc | 58 ++ .../applications/cmdutils/whoami/resource.h | 39 + .../applications/cmdutils/whoami/whoami.c | 899 ++++++++++++++++++ .../applications/cmdutils/whoami/whoami.rc | 22 + 7 files changed, 1080 insertions(+) create mode 100644 reactos/base/applications/cmdutils/whoami/CMakeLists.txt create mode 100644 reactos/base/applications/cmdutils/whoami/lang/en-US.rc create mode 100644 reactos/base/applications/cmdutils/whoami/lang/es-ES.rc create mode 100644 reactos/base/applications/cmdutils/whoami/resource.h create mode 100644 reactos/base/applications/cmdutils/whoami/whoami.c create mode 100644 reactos/base/applications/cmdutils/whoami/whoami.rc diff --git a/reactos/base/applications/cmdutils/CMakeLists.txt b/reactos/base/applications/cmdutils/CMakeLists.txt index f0214905aca..782baed5919 100644 --- a/reactos/base/applications/cmdutils/CMakeLists.txt +++ b/reactos/base/applications/cmdutils/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(reg) add_subdirectory(sort) add_subdirectory(taskkill) add_subdirectory(tree) +add_subdirectory(whoami) add_subdirectory(wmic) add_subdirectory(wscript) add_subdirectory(xcopy) diff --git a/reactos/base/applications/cmdutils/whoami/CMakeLists.txt b/reactos/base/applications/cmdutils/whoami/CMakeLists.txt new file mode 100644 index 00000000000..60094134b38 --- /dev/null +++ b/reactos/base/applications/cmdutils/whoami/CMakeLists.txt @@ -0,0 +1,5 @@ + +add_executable(whoami whoami.c whoami.rc) +set_module_type(whoami win32cui UNICODE) +add_importlibs(whoami user32 secur32 advapi32 msvcrt kernel32) +add_cd_file(TARGET whoami DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/cmdutils/whoami/lang/en-US.rc b/reactos/base/applications/cmdutils/whoami/lang/en-US.rc new file mode 100644 index 00000000000..b041811ded7 --- /dev/null +++ b/reactos/base/applications/cmdutils/whoami/lang/en-US.rc @@ -0,0 +1,56 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +STRINGTABLE +BEGIN + IDS_USER_HEADER "USER INFORMATION" + IDS_GROU_HEADER "GROUP INFORMATION" + IDS_PRIV_HEADER "PRIVILEGES INFORMATION" + IDS_COL_USER_NAME "User Name" + IDS_COL_GROUP_NAME "Group Name" + IDS_COL_TYPE "Type" + IDS_COL_SID "SID" + IDS_COL_ATTRIB "Attributes" + IDS_COL_PRIV_NAME "Privilege Name" + IDS_COL_DESCRIPTION "Description" + IDS_COL_STATE "State" + IDS_TP_WELL_KNOWN_GROUP "Well-known group" + IDS_TP_ALIAS "Alias" + IDS_TP_LABEL "Label" + + /* [!] important note from the programmer: the program tries to remove + the last ', ' after concatenating, so keep than in mind when translating. + + you can test your translation of these attributes by using 'whoami /groups' */ + + IDS_ATTR_GROUP_MANDATORY "Mandatory group, " + IDS_ATTR_GROUP_ENABLED_BY_DEFAULT "Enabled by default, " + IDS_ATTR_GROUP_ENABLED "Enabled group, " + IDS_ATTR_GROUP_OWNER "Group owner, " + IDS_UNKNOWN_DESCRIPTION "???" + IDS_STATE_ENABLED "Enabled" + IDS_STATE_DISABLED "Disabled" + IDS_ERROR_UPN "ERROR: Unable to get User Principal Name (UPN) as the current logged-on user\nis not a domain user.\n" + IDS_ERROR_FQDN "ERROR: Unable to get Fully Qualified Distinguished Name (FQDN) as the current\nlogged-on user is not a domain user.\n" + IDS_ERROR_VALUEXPECTED "ERROR: Invalid syntax. Value expected for '/fo'.\nType ""WHOAMI /?"" for usage.\n" + IDS_ERROR_VALUENOTALLOWED "ERROR: Invalid syntax. '%s' value is not allowed for '/fo' option.\nType ""WHOAMI /?"" for usage.\n" + IDS_ERROR_1TIMES "ERROR: Invalid syntax. '%s' option is not allowed more than '1' time(s).\nType ""WHOAMI /?"" for usage.\n" + IDS_ERROR_INVALIDSYNTAX "ERROR: Invalid syntax.\nType ""WHOAMI /?"" for usage.\n" + IDS_ERROR_INVALIDARG "ERROR: Invalid argument/option - '%s'.\nType ""WHOAMI /?"" for usage.\n" + IDS_ERROR_NH_LIST "ERROR: /NH switch cannot be used with the LIST format.\nType ""WHOAMI /?"" for usage.\n" + IDS_HELP "DESCRIPTION:\n\ + Display user, group and privileges information for the local logged-on user.\n\ + If no arguments are provided, displays the current domain and user name.\n\ + \n\ + Available output formats for the '/fo' option are 'csv', 'list' and 'table'.\n\ + Use '/nh' to hide headers. By default the data is displayed in a table.\n\ + \n\ + SYNTAX:\n\ + whoami [/upn | /fqdn | /logonid] \n\ + whoami {[/user] [/groups] [/priv]} [/fo ] [/nh] \n\ + whoami /all [/fo ] [/nh] \n\ + \n\ + EXAMPLES: \n\ + whoami /groups /priv /nh /fo csv \n\ + whoami /logonid \n\ + whoami \n" +END diff --git a/reactos/base/applications/cmdutils/whoami/lang/es-ES.rc b/reactos/base/applications/cmdutils/whoami/lang/es-ES.rc new file mode 100644 index 00000000000..8e33cceca75 --- /dev/null +++ b/reactos/base/applications/cmdutils/whoami/lang/es-ES.rc @@ -0,0 +1,58 @@ +/* Spanish translation by Swyter */ +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +STRINGTABLE +BEGIN + IDS_USER_HEADER "INFORMACIÓN DE USUARIO" + IDS_GROU_HEADER "INFORMACIÓN DE GRUPO" + IDS_PRIV_HEADER "INFORMACIÓN DE PRIVILEGIOS" + IDS_COL_USER_NAME "Nombre de usuario" + IDS_COL_GROUP_NAME "Nombre de grupo" + IDS_COL_TYPE "Tipo" + IDS_COL_SID "SID" + IDS_COL_ATTRIB "Atributos" + IDS_COL_PRIV_NAME "Nombre de privilegio" + IDS_COL_DESCRIPTION "Descripción" + IDS_COL_STATE "Estado" + IDS_TP_WELL_KNOWN_GROUP "Grupo conocido" + IDS_TP_ALIAS "Alias" + IDS_TP_LABEL "Etiqueta" + + /* [!] important note from the programmer: the program tries to remove + the last ', ' after concatenating, so keep than in mind when translating. + + you can test your translation of these attributes by using 'whoami /groups' */ + + IDS_ATTR_GROUP_MANDATORY "Grupo obligatorio, " + IDS_ATTR_GROUP_ENABLED_BY_DEFAULT "Habilitado de manera predeterminada, " + IDS_ATTR_GROUP_ENABLED "Grupo habilitado, " + IDS_ATTR_GROUP_OWNER "Propietario de grupo, " + IDS_UNKNOWN_DESCRIPTION "¿?" + IDS_STATE_ENABLED "Habilitada" + IDS_STATE_DISABLED "Deshabilitado" + IDS_ERROR_UPN "ERROR: no se puede obtener el nombre principal de usuario (UPN) porque el\nusuario que ha iniciado sesión no es un usuario de dominio.\n" + IDS_ERROR_FQDN "ERROR: no se puede obtener el nombre distintivo completo (FQDN) porque el usuario\nque ha iniciado sesión no es un usuario de dominio.\n" + IDS_ERROR_VALUEXPECTED "ERROR: Sintaxis no válida. Se esperaba un valor para ""/fo"".\nEscriba ""WHOAMI /?"" para su uso.\n" + IDS_ERROR_VALUENOTALLOWED "ERROR: Sintaxis no válida. El valor ""%s"" no está permitido para la opción ""/fo"".\nEscriba ""WHOAMI /?"" para su uso.\n" + IDS_ERROR_1TIMES "ERROR: Sintaxis no válida. La opción ""%s"" no está permitida más de ""1"" veces.\nEscriba ""WHOAMI /?"" para su uso.\n" + IDS_ERROR_INVALIDSYNTAX "ERROR: sintaxis no válida.\nEscriba ""WHOAMI /?"" para obtener detalles de uso.\n" + IDS_ERROR_INVALIDARG "ERROR: Argumento u opción no válido - ""%s"".\nEscriba ""WHOAMI /?"" para su uso.\n" + IDS_ERROR_NH_LIST "ERROR: no se puede usar el modificador /NH con el formato LIST.\nEscriba ""WHOAMI /?"" para obtener detalles de uso.\n" + IDS_HELP "DESCRIPCIÓN:\n\ + Muestra información sobre el usuario local, sus privilegios y grupos.\n\ + Si no se añaden argumentos se mostrará el usuario y dominio actual.\n\ + \n\ + Los formatos disponibles para '/fo' son 'csv', 'list' y 'table'.\n\ + Por defecto los datos se muestran en una tabla (opción 'table').\n\ + Puedes utilizar '/nh' para ocultar los encabezados. \n\ + \n\ + SINTAXIS:\n\ + whoami [/upn | /fqdn | /logonid] \n\ + whoami {[/user] [/groups] [/priv]} [/fo ] [/nh] \n\ + whoami /all [/fo ] [/nh] \n\ + \n\ + EJEMPLOS: \n\ + whoami /groups /priv /nh /fo csv \n\ + whoami /logonid \n\ + whoami \n" +END diff --git a/reactos/base/applications/cmdutils/whoami/resource.h b/reactos/base/applications/cmdutils/whoami/resource.h new file mode 100644 index 00000000000..df2d1d09610 --- /dev/null +++ b/reactos/base/applications/cmdutils/whoami/resource.h @@ -0,0 +1,39 @@ +#pragma once + +#define IDS_USER_HEADER 0 +#define IDS_GROU_HEADER 1 +#define IDS_PRIV_HEADER 2 + +#define IDS_COL_USER_NAME 3 +#define IDS_COL_GROUP_NAME 4 +#define IDS_COL_TYPE 5 +#define IDS_COL_SID 6 +#define IDS_COL_ATTRIB 7 +#define IDS_COL_PRIV_NAME 8 +#define IDS_COL_DESCRIPTION 9 +#define IDS_COL_STATE 10 + +#define IDS_TP_WELL_KNOWN_GROUP 11 +#define IDS_TP_ALIAS 12 +#define IDS_TP_LABEL 13 + +#define IDS_ATTR_GROUP_MANDATORY 14 +#define IDS_ATTR_GROUP_ENABLED_BY_DEFAULT 15 +#define IDS_ATTR_GROUP_ENABLED 16 +#define IDS_ATTR_GROUP_OWNER 17 + +#define IDS_UNKNOWN_DESCRIPTION 18 + +#define IDS_STATE_ENABLED 19 +#define IDS_STATE_DISABLED 20 + +#define IDS_ERROR_UPN 21 +#define IDS_ERROR_FQDN 22 +#define IDS_ERROR_VALUEXPECTED 23 +#define IDS_ERROR_VALUENOTALLOWED 24 +#define IDS_ERROR_1TIMES 25 +#define IDS_ERROR_INVALIDSYNTAX 26 +#define IDS_ERROR_INVALIDARG 27 +#define IDS_ERROR_NH_LIST 28 + +#define IDS_HELP 29 diff --git a/reactos/base/applications/cmdutils/whoami/whoami.c b/reactos/base/applications/cmdutils/whoami/whoami.c new file mode 100644 index 00000000000..c4cbbe4836f --- /dev/null +++ b/reactos/base/applications/cmdutils/whoami/whoami.c @@ -0,0 +1,899 @@ +/* + * PROJECT: ReactOS Whoami + * LICENSE: GPL - See COPYING in the top level directory + * FILE: base/applications/cmdutils/whoami/whoami.c + * PURPOSE: Displays information about the current local user, groups and privileges. + * PROGRAMMERS: Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com) + */ + + +#define SECURITY_WIN32 +#include +#include + +#include + +#include "resource.h" + +BOOL NoHeader = FALSE; +UINT NoHeaderArgCount = 0; +UINT PrintFormatArgCount = 0; + +enum +{ + undefined, + table, + list, + csv +} PrintFormat = undefined; + + +BOOL GetArgument(WCHAR* arg, int argc, WCHAR* argv[]) +{ + int i; + + if (!arg) + goto BailOut; + + for (i = 1; i < argc; i++) + { + if (wcsicmp(argv[i], arg) == 0) + return TRUE; + } + + BailOut: + return FALSE; +} + +/* blanking out the accepted modifiers will make filtering easier later on */ +void BlankArgument(int argc, WCHAR* argv[]) +{ + argv[argc] = L""; +} + +/* helper functions; let's keep it tidy to avoid redundancies */ + +LPWSTR WhoamiGetUser(EXTENDED_NAME_FORMAT NameFormat) +{ + LPWSTR UsrBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH); + ULONG UsrSiz = MAX_PATH; + + if (UsrBuf && GetUserNameExW(NameFormat, UsrBuf, &UsrSiz)) + { + CharLowerW(UsrBuf); + return UsrBuf; + } + + return NULL; +} + +BOOL WhoamiFree(VOID* Buffer) +{ + return HeapFree(GetProcessHeap(), 0, Buffer); +} + + +VOID* WhoamiGetTokenInfo(TOKEN_INFORMATION_CLASS TokenType) +{ + HANDLE hToken = 0; + DWORD dwLength = 0; + VOID* pTokenInfo = 0; + + if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) + { + GetTokenInformation(hToken, + TokenType, + NULL, + dwLength, + &dwLength); + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + pTokenInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); + if (pTokenInfo == NULL) + { + wprintf(L"ERROR: not enough memory to allocate the token structure.\r\n"); + exit(1); + } + } + + if (!GetTokenInformation(hToken, TokenType, + (LPVOID)pTokenInfo, + dwLength, + &dwLength)) + { + wprintf(L"ERROR 0x%x: could not get token information.\r\n", GetLastError()); + exit(1); + } + + CloseHandle(hToken); + } + + return pTokenInfo; +} + +LPWSTR WhoamiLoadRcString(INT ResId) +{ + #define RC_STRING_MAX_SIZE 850 + static WCHAR TmpBuffer[RC_STRING_MAX_SIZE]; + + LoadStringW(GetModuleHandleW(NULL), ResId, TmpBuffer, RC_STRING_MAX_SIZE); + + return TmpBuffer; +} + +void WhoamiPrintHeader(int HeaderId) +{ + PWSTR Header = WhoamiLoadRcString(HeaderId); + DWORD Length = wcslen(Header); + + if (NoHeader || PrintFormat == csv) + return; + + wprintf(L"\n%s\n", Header); + + while (Length--) + wprintf(L"-"); + + _putws(L"\n"); +} + +typedef struct +{ + UINT Rows; + UINT Cols; + LPWSTR Content[1]; +} WhoamiTable; + +/* create and prepare a new table for printing */ +WhoamiTable *WhoamiAllocTable(UINT Rows, UINT Cols) +{ + WhoamiTable *pTable = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(WhoamiTable) + sizeof(LPWSTR) * Rows * Cols); + + // wprintf(L"DEBUG: Allocating %dx%d elem table for printing.\r\n\r\n", Rows, Cols); + + if (!pTable) + { + wprintf(L"ERROR: Not enough memory for displaying the table."); + exit(1); + } + + pTable->Rows = Rows; + pTable->Cols = Cols; + + return pTable; +} + +/* allocate and fill a new entry in the table */ +void WhoamiSetTable(WhoamiTable *pTable, WCHAR *Entry, UINT Row, UINT Col) +{ + LPWSTR Target = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + 1 + wcslen(Entry) * sizeof(Entry[0])); + + // wprintf(L"DEBUG: Setting table value '%lp' '%ls' for %lu %lu.\n", entry, entry, row, col); + + if (!Target) + exit(1); + + wcscpy(Target, Entry); + + pTable->Content[Row * pTable->Cols + Col] = Target; +} + +/* fill a new entry in the table */ +void WhoamiSetTableDyn(WhoamiTable *pTable, WCHAR *Entry, UINT Row, UINT Col) +{ + pTable->Content[Row * pTable->Cols + Col] = Entry; +} + +/* print and deallocate the table */ +void WhoamiPrintTable(WhoamiTable *pTable) +{ + UINT i, j; + UINT CurRow, CurCol; + UINT *ColLength; + + + if (!pTable) + { + wprintf(L"ERROR: The table passed for display is empty."); + exit(1); + } + + /* if we are going to print a *list* or *table*; take note of the total + column size, as we will need it later on when printing them in a tabular + fashion, according to their windows counterparts */ + + if (PrintFormat != csv) + { + ColLength = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(UINT) * pTable->Cols); + + if (PrintFormat == list) + { + for (j = 0; j < pTable->Cols; j++) + if (pTable->Content[j]) + { + UINT ThisLength = wcslen(pTable->Content[j]); + + /* now that we're here, seize the opportunity and add those pesky ":" */ + pTable->Content[j][ThisLength++] = L':'; + pTable->Content[j][ThisLength] = UNICODE_NULL; + + ColLength[0] = max(ThisLength, ColLength[0]); + } + } + else + { + for (j = 0; j < pTable->Cols; j++) + for (i = 0; i < pTable->Rows; i++) + if (pTable->Content[i * pTable->Cols + j]) + { + UINT ThisLength = wcslen(pTable->Content[i * pTable->Cols + j]); + ColLength[j] = max(ThisLength, ColLength[j]); + } + } + } + + switch (PrintFormat) + { + case csv: + { + for (i = 0; i < pTable->Rows; i++) + { + if (!pTable->Content[i * pTable->Cols]) + continue; + + /* if the user especified /nh then skip the column labels */ + if (NoHeader && i == 0) + continue; + + for (j = 0; j < pTable->Cols; j++) + { + if (pTable->Content[i * pTable->Cols + j]) + { + wprintf(L"\"%s\"%s", + pTable->Content[i * pTable->Cols + j], + (j+1 < pTable->Cols ? L"," : L"")); + } + } + wprintf(L"\n"); + } + + break; + + } + + case list: + { + UINT FinalRow = 0; + + /* 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 */ + for (CurRow = 1; CurRow < pTable->Rows; CurRow++) + { + /* if the first member of this row isn't available, then forget it */ + if (!pTable->Content[CurRow * pTable->Cols]) + continue; + + FinalRow = CurRow; + } + + for (CurRow = 1; CurRow < pTable->Rows; CurRow++) + { + /* if the first member of this row isn't available, then forget it */ + if (!pTable->Content[CurRow * pTable->Cols]) + continue; + + /* if the user especified /nh then skip the column labels */ + if (NoHeader && i == 0) + continue; + + for (CurCol = 0; CurCol < pTable->Cols; CurCol++) + { + wprintf(L"%-*s %s\n", + ColLength[0], + pTable->Content[CurCol], + pTable->Content[CurRow * pTable->Cols + CurCol]); + } + + /* don't add two carriage returns at the very end */ + if (CurRow != FinalRow) + wprintf(L"\n"); + } + + break; + } + + + case table: + default: + { + for (i = 0; i < pTable->Rows; i++) + { + /* if the first member of this row isn't available, then forget it */ + if (!pTable->Content[i * pTable->Cols]) + continue; + + /* if the user especified /nh then skip the column labels too */ + if (NoHeader && i == 0) + continue; + + for (j = 0; j < pTable->Cols; j++) + { + if (pTable->Content[i * pTable->Cols + j]) + { + wprintf(L"%-*s ", ColLength[j], pTable->Content[i * pTable->Cols + j]); + } + } + wprintf(L"\n"); + + /* add the cute underline thingie for the table header */ + if (i == 0) + { + for (j = 0; j < pTable->Cols; j++) + { + DWORD Length = ColLength[j]; + + while (Length--) + wprintf(L"="); + + /* a spacing between all the columns except for the last one */ + if (pTable->Cols != (i + 1)) + wprintf(L" "); + } + + wprintf(L"\n"); + } + } + + } + } + + /* fixme: when many tables are displayed in a single run we + have to sandwich carriage returns in between. */ + // if (!final_entry) + wprintf(L"\n"); + + for (i = 0; i < pTable->Rows; i++) + for (j = 0; j < pTable->Cols; j++) + WhoamiFree(pTable->Content[i * pTable->Cols + j]); + + WhoamiFree(pTable); +} + +int WhoamiLogonId(void) +{ + PTOKEN_GROUPS pGroupInfo = (PTOKEN_GROUPS) WhoamiGetTokenInfo(TokenGroups); + DWORD dwIndex = 0; + LPWSTR pSidStr = 0; + PSID pSid = 0; + + if (pGroupInfo) + { + /* lets see if we can find the logon SID in that list, should be there */ + for (dwIndex = 0; dwIndex < pGroupInfo->GroupCount; dwIndex++) + { + if ((pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) + { + pSid = pGroupInfo->Groups[dwIndex].Sid; + } + } + + if (!pSid || !ConvertSidToStringSidW(pSid, &pSidStr)) + { + wprintf(L"ERROR: Couldn't convert the logon SID to a string.\n"); + return 1; + } + else + { + /* let's show our converted logon SID */ + wprintf(L"%s\n", pSidStr); + } + } + + /* cleanup our allocations */ + if (pSidStr) + LocalFree(pSidStr); + + if (pGroupInfo) + WhoamiFree(pGroupInfo); + + return 0; +} + +int WhoamiUser(void) +{ + PTOKEN_USER pUserInfo = (PTOKEN_USER) WhoamiGetTokenInfo(TokenUser); + LPWSTR pUserStr = WhoamiGetUser(NameSamCompatible); + LPWSTR pSidStr = NULL; + + if (pUserInfo && pUserStr) + { + WhoamiTable *UserTable = WhoamiAllocTable(2, 2); + + WhoamiPrintHeader(IDS_USER_HEADER); + + /* set the column labels */ + WhoamiSetTable(UserTable, WhoamiLoadRcString(IDS_COL_USER_NAME), 0, 0); + WhoamiSetTable(UserTable, WhoamiLoadRcString(IDS_COL_SID), 0, 1); + + ConvertSidToStringSidW(pUserInfo->User.Sid, &pSidStr); + + /* set the values for our single row of data */ + WhoamiSetTable(UserTable, pUserStr, 1, 0); + WhoamiSetTable(UserTable, pSidStr, 1, 1); + + WhoamiPrintTable(UserTable); + } + else + { + return 1; + } + + /* cleanup our allocations */ + if (pSidStr) + LocalFree(pSidStr); + + if (pUserInfo) + WhoamiFree(pUserInfo); + + if (pUserStr) + WhoamiFree(pUserStr); + + return 0; +} + +int WhoamiGroups(void) +{ + DWORD dwIndex = 0; + LPWSTR pSidStr = 0; + + static WCHAR szGroupName[255] = {0}; + static WCHAR szDomainName[255] = {0}; + + DWORD cchGroupName = _countof(szGroupName); + DWORD cchDomainName = _countof(szGroupName); + + SID_NAME_USE Use = 0; + BYTE SidNameUseStr[12] = + { + /* SidTypeUser */ -1, + /* SidTypeGroup */ -1, + /* SidTypeDomain */ -1, + /* SidTypeUser */ -1, + /* SidTypeAlias */ IDS_TP_ALIAS, + /* SidTypeWellKnownGroup */ IDS_TP_WELL_KNOWN_GROUP, + /* SidTypeDeletedAccount */ -1, + /* SidTypeInvalid */ -1, + /* SidTypeUnknown */ -1, + /* SidTypeComputer */ -1, + /* SidTypeLabel */ IDS_TP_LABEL + }; + + PTOKEN_GROUPS pGroupInfo = (PTOKEN_GROUPS)WhoamiGetTokenInfo(TokenGroups); + + if (pGroupInfo) + { + /* the header is the first (0) row, so we start in the second one (1) */ + UINT PrintingRow = 1; + + WhoamiTable *GroupTable = WhoamiAllocTable(pGroupInfo->GroupCount + 1, 4); + + WhoamiPrintHeader(IDS_GROU_HEADER); + + WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_GROUP_NAME), 0, 0); + WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_TYPE), 0, 1); + WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_SID), 0, 2); + WhoamiSetTable(GroupTable, WhoamiLoadRcString(IDS_COL_ATTRIB), 0, 3); + + for (dwIndex = 0; dwIndex < pGroupInfo->GroupCount; dwIndex++) + { + LookupAccountSidW(NULL, + pGroupInfo->Groups[dwIndex].Sid, + (LPWSTR)&szGroupName, + &cchGroupName, + (LPWSTR)&szDomainName, + &cchDomainName, + &Use); + + /* the original tool seems to limit the list to these kind of SID items */ + if ((Use == SidTypeWellKnownGroup || Use == SidTypeAlias || + Use == SidTypeLabel) && !(pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)) + { + wchar_t tmpBuffer[666]; + + /* looks like windows treats 0x60 as 0x7 for some reason, let's just nod and call it a day: + 0x60 is SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED + 0x07 is SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED */ + + if (pGroupInfo->Groups[dwIndex].Attributes == 0x60) + pGroupInfo->Groups[dwIndex].Attributes = 0x07; + + /* 1- format it as DOMAIN\GROUP if the domain exists, or just GROUP if not */ + _snwprintf((LPWSTR)&tmpBuffer, + 666, + L"%s%s%s", + szDomainName, + cchDomainName ? L"\\" : L"", + szGroupName); + + WhoamiSetTable(GroupTable, tmpBuffer, PrintingRow, 0); + + /* 2- let's find out the group type by using a simple lookup table for lack of a better method */ + WhoamiSetTable(GroupTable, WhoamiLoadRcString(SidNameUseStr[Use]), PrintingRow, 1); + + /* 3- turn that SID into text-form */ + ConvertSidToStringSidW(pGroupInfo->Groups[dwIndex].Sid, &pSidStr); + + WhoamiSetTable(GroupTable, pSidStr, PrintingRow, 2); + + LocalFree(pSidStr); + + /* 4- reuse that buffer for appending the attributes in text-form at the very end */ + ZeroMemory(tmpBuffer, 666); + + if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_MANDATORY) + StringCchCat(tmpBuffer, 666, WhoamiLoadRcString(IDS_ATTR_GROUP_MANDATORY)); + if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + StringCchCat(tmpBuffer, 666, WhoamiLoadRcString(IDS_ATTR_GROUP_ENABLED_BY_DEFAULT)); + if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_ENABLED) + StringCchCat(tmpBuffer, 666, WhoamiLoadRcString(IDS_ATTR_GROUP_ENABLED)); + if (pGroupInfo->Groups[dwIndex].Attributes & SE_GROUP_OWNER) + StringCchCat(tmpBuffer, 666, WhoamiLoadRcString(IDS_ATTR_GROUP_OWNER)); + + /* remove the last comma (', ' which is 2 wchars) of the buffer, let's keep it simple */ + tmpBuffer[max(wcslen(tmpBuffer) - 2, 0)] = UNICODE_NULL; + + WhoamiSetTable(GroupTable, tmpBuffer, PrintingRow, 3); + + PrintingRow++; + } + + /* reset the buffers so that we can reuse them */ + ZeroMemory(szGroupName, 255); + ZeroMemory(szDomainName, 255); + + cchGroupName = 255; + cchDomainName = 255; + } + + WhoamiPrintTable(GroupTable); + } + else + { + return 1; + } + + /* cleanup our allocations */ + if (pGroupInfo) + WhoamiFree((LPVOID)pGroupInfo); + + return 0; +} + +int WhoamiPriv(void) +{ + PTOKEN_PRIVILEGES pPrivInfo = (PTOKEN_PRIVILEGES) WhoamiGetTokenInfo(TokenPrivileges); + + if (pPrivInfo) + { + DWORD dwResult = 0, dwIndex = 0; + + WhoamiTable *PrivTable = WhoamiAllocTable(pPrivInfo->PrivilegeCount + 1, 3); + + WhoamiPrintHeader(IDS_PRIV_HEADER); + + WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_COL_PRIV_NAME), 0, 0); + WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_COL_DESCRIPTION), 0, 1); + WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_COL_STATE), 0, 2); + + for (dwIndex = 0; dwIndex < pPrivInfo->PrivilegeCount; dwIndex++) + { + PWSTR PrivName = NULL, DispName = NULL; + DWORD PrivNameSize = 0, DispNameSize = 0; + BOOL ret = FALSE; + + ret = LookupPrivilegeNameW(NULL, + &pPrivInfo->Privileges[dwIndex].Luid, + NULL, + &PrivNameSize); + + PrivName = HeapAlloc(GetProcessHeap(), 0, ++PrivNameSize*sizeof(WCHAR)); + + LookupPrivilegeNameW(NULL, + &pPrivInfo->Privileges[dwIndex].Luid, + PrivName, + &PrivNameSize); + + WhoamiSetTableDyn(PrivTable, PrivName, dwIndex + 1, 0); + + ret = LookupPrivilegeDisplayNameW(NULL, PrivName, NULL, &DispNameSize, &dwResult); + + if (!ret || GetLastError() == ERROR_NO_SUCH_PRIVILEGE) + { + DispName = HeapAlloc(GetProcessHeap(), 0, ++DispNameSize * sizeof(WCHAR)); + + LookupPrivilegeDisplayNameW(NULL, PrivName, DispName, &DispNameSize, &dwResult); + + //wprintf(L"DispName: %d %x '%s'\n", DispNameSize, GetLastError(), DispName); + + WhoamiSetTableDyn(PrivTable, DispName, dwIndex + 1, 1); + } + else + { + WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_UNKNOWN_DESCRIPTION), dwIndex + 1, 1); + } + + if (pPrivInfo->Privileges[dwIndex].Attributes & SE_PRIVILEGE_ENABLED) + WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_STATE_ENABLED), dwIndex + 1, 2); + else + WhoamiSetTable(PrivTable, WhoamiLoadRcString(IDS_STATE_DISABLED), dwIndex + 1, 2); + } + + WhoamiPrintTable(PrivTable); + } + else + { + return 1; + } + + /* cleanup our allocations */ + if (pPrivInfo) + WhoamiFree(pPrivInfo); + + return 0; +} + +int wmain(int argc, WCHAR* argv[]) +{ + INT i; + BYTE WamBit = 0; + + #define WAM_USER 1<<0 + #define WAM_GROUPS 1<<1 + #define WAM_PRIV 1<<2 + + + /* * * * * * * * * * * * * * * * + * A: no parameters whatsoever */ + + if (argc == 1) + { + /* if there's no arguments just choose the simple path and display the user's identity in lowercase */ + LPWSTR UserBuffer = WhoamiGetUser(NameSamCompatible); + + if (UserBuffer) + { + wprintf(L"%s\n", UserBuffer); + WhoamiFree(UserBuffer); + return 0; + } + else + { + return 1; + } + } + + /* first things first-- let's detect and manage both printing modifiers (/fo and /nh) */ + for (i = 1; i < argc; i++) + { + if (wcsicmp(argv[i], L"/nh") == 0) + { + NoHeaderArgCount++; + + if (NoHeader != TRUE) + { + NoHeader = TRUE; + // wprintf(L"Headers disabled!\n"); + BlankArgument(i, argv); + } + } + } + + for (i = 1; i < argc; i++) + { + if (wcsicmp(argv[i], L"/fo") == 0) + { + if ((i + 1) < argc) + { + // wprintf(L"exists another param after /fo\n"); + + PrintFormatArgCount++; + + if (wcsicmp(argv[i + 1], L"table") == 0 && PrintFormat != table) + { + PrintFormat = table; + // wprintf(L"Changed to table format\n"); + BlankArgument(i, argv); + BlankArgument(i + 1, argv); + } + else if (wcsicmp(argv[i + 1], L"list") == 0 && PrintFormat != list) + { + PrintFormat = list; + // wprintf(L"Changed to list format\n"); + BlankArgument(i, argv); + BlankArgument(i + 1, argv); + + /* looks like you can't use the "/fo list /nh" options together + for some stupid reason */ + if (PrintFormat == list && NoHeader == TRUE) + { + wprintf(WhoamiLoadRcString(IDS_ERROR_NH_LIST)); + return 1; + } + } + else if (wcsicmp(argv[i + 1], L"csv") == 0 && PrintFormat != csv) + { + PrintFormat = csv; + // wprintf(L"Changed to csv format\n"); + BlankArgument(i, argv); + BlankArgument(i + 1, argv); + } + /* /nh or /fo after /fo isn't parsed as a value */ + else if (wcsicmp(argv[i + 1], L"/nh") == 0 || wcsicmp(argv[i + 1], L"/fo") == 0 + + /* same goes for the other named options, not ideal, but works */ + || wcsicmp(argv[i + 1], L"/priv") == 0 + || wcsicmp(argv[i + 1], L"/groups") == 0 + || wcsicmp(argv[i + 1], L"/user") == 0 + || wcsicmp(argv[i + 1], L"/all") == 0 + || wcsicmp(argv[i + 1], L"") == 0) + { + goto FoValueExpected; + } + else + { + wprintf(WhoamiLoadRcString(IDS_ERROR_VALUENOTALLOWED), argv[i + 1]); + return 1; + } + } + else + { + FoValueExpected: + + wprintf(WhoamiLoadRcString(IDS_ERROR_VALUEXPECTED)); + return 1; + } + } + } + + if (NoHeaderArgCount >= 2) + { + wprintf(WhoamiLoadRcString(IDS_ERROR_1TIMES), L"/nh"); + return 1; + } + /* special case when there's just a /nh as argument; it outputs nothing */ + else if (NoHeaderArgCount == 1 && argc == 2) + { + return 0; + } + + if (PrintFormatArgCount >= 2) + { + wprintf(WhoamiLoadRcString(IDS_ERROR_1TIMES), L"/fo"); + return 1; + } + /* if there's just /fo ... call it invalid */ + else if (PrintFormatArgCount == 1 && argc == 3) + { + goto InvalidSyntax; + } + + /* * * * * * * * * * * * * * + * B: one single parameter */ + + if (argc == 2) + { + /* now let's try to parse the triumvirate of simpler, single (1) arguments... plus help */ + if (wcsicmp(argv[1], L"/?") == 0) + { + wprintf(WhoamiLoadRcString(IDS_HELP)); + return 0; + } + + else if (wcsicmp(argv[1], L"/upn") == 0) + { + LPWSTR UserBuffer = WhoamiGetUser(NameUserPrincipal); + + if (UserBuffer) + { + wprintf(L"%s\n", UserBuffer); + WhoamiFree(UserBuffer); + return 0; + } + else + { + wprintf(WhoamiLoadRcString(IDS_ERROR_UPN)); + return 1; + } + } + + else if (wcsicmp(argv[1], L"/fqdn") == 0) + { + LPWSTR UserBuffer = WhoamiGetUser(NameFullyQualifiedDN); + + if (UserBuffer) + { + wprintf(L"%s\n", UserBuffer); + WhoamiFree(UserBuffer); + return 0; + } + else + { + wprintf(WhoamiLoadRcString(IDS_ERROR_FQDN)); + return 1; + } + } + + else if (wcsicmp(argv[1], L"/logonid") == 0) + { + return WhoamiLogonId(); + } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * C: One main parameter with extra tasty modifiers to play with */ + + /* sometimes is just easier to whitelist for lack of a better method */ + for (i=1; i + +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define REACTOS_STR_FILE_DESCRIPTION "whoami: display information about logged-on users" +#define REACTOS_STR_INTERNAL_NAME "whoami" +#define REACTOS_STR_ORIGINAL_FILENAME "whoami.exe" +#define REACTOS_STR_COMPANY_NAME "Ismael Ferreras Morezuelas" + +#include + +/* UTF-8 */ +#pragma code_page(65001) + +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif +#ifdef LANGUAGE_ES_ES + #include "lang/es-ES.rc" +#endif