Particularly it implements '/logonid' and '/user' options, as well as adds stubs for '/upn' and '/fqdn'.
-- v4: whoami: Refactor and add more commands.
From: Maxim Karasev mxkrsv@etersoft.ru
--- dlls/secur32/secur32.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c index d703757bae7..df9c864b4ea 100644 --- a/dlls/secur32/secur32.c +++ b/dlls/secur32/secur32.c @@ -1144,6 +1144,7 @@ BOOLEAN WINAPI GetUserNameExW( case NameCanonicalEx: case NameServicePrincipal: case NameDnsDomain: + FIXME("stub NameFormat %d", NameFormat); SetLastError(ERROR_NONE_MAPPED); return FALSE;
From: Maxim Karasev mxkrsv@etersoft.ru
Particularly it implements '/logonid' and '/user' options, as well as adds stubs for '/upn' and '/fqdn'. --- programs/whoami/Makefile.in | 2 +- programs/whoami/main.c | 295 ++++++++++++++++++++++++++++++++---- 2 files changed, 269 insertions(+), 28 deletions(-)
diff --git a/programs/whoami/Makefile.in b/programs/whoami/Makefile.in index 4898a96f456..5be08c702a8 100644 --- a/programs/whoami/Makefile.in +++ b/programs/whoami/Makefile.in @@ -1,5 +1,5 @@ MODULE = whoami.exe -IMPORTS = secur32 +IMPORTS = secur32 advapi32
EXTRADLLFLAGS = -mconsole -municode
diff --git a/programs/whoami/main.c b/programs/whoami/main.c index 7e93de998ef..75bf96151d2 100644 --- a/programs/whoami/main.c +++ b/programs/whoami/main.c @@ -1,5 +1,6 @@ /* * Copyright 2020 Brendan Shanks for CodeWeavers + * Copyright 2023 Maxim Karasev mxkrsv@etersoft.ru * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,78 +17,318 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define WIN32_LEAN_AND_MEAN #define SECURITY_WIN32 #include <windows.h> #include <security.h> +#include <sddl.h> +#include <stdarg.h>
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(whoami);
-static int output_write(const WCHAR* str, int len) +/* Behaves like the regular wprintf, but is able to fall back to the OEMCP + * when redirected. + * Most importantly, it prints fully in Unicode by default, + * so any language characters are shown regardless of the current locale. + */ +static int unicode_wprintf(const WCHAR *fmt, ...) { DWORD count; - if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, len, &count, NULL)) + BOOL status; + int formatted_len; + int formatted_len2; + WCHAR *formatted; + va_list args; + + va_start(args, fmt); + + formatted_len = vswprintf(NULL, 0, fmt, args); + + formatted = HeapAlloc(GetProcessHeap(), 0, (formatted_len + 1) * sizeof(WCHAR)); + + formatted_len2 = vswprintf(formatted, formatted_len + 1, fmt, args); + if (formatted_len2 != formatted_len) + { + ERR("formatted_len mismatch: %d != %d\n", formatted_len, formatted_len2); + return 0; + } + + va_end(args); + + status = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), formatted, formatted_len, + &count, NULL); + if (!status) { DWORD lenA; - char* strA; + char *strA;
/* On Windows WriteConsoleW() fails if the output is redirected. So fall * back to WriteFile() with OEM code page. */ - lenA = WideCharToMultiByte(GetOEMCP(), 0, str, len, + lenA = WideCharToMultiByte(GetOEMCP(), 0, formatted, formatted_len, NULL, 0, NULL, NULL); strA = HeapAlloc(GetProcessHeap(), 0, lenA); if (!strA) return 0;
- WideCharToMultiByte(GetOEMCP(), 0, str, len, strA, lenA, + WideCharToMultiByte(GetOEMCP(), 0, formatted, formatted_len, strA, lenA, NULL, NULL); WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), strA, lenA, &count, FALSE); HeapFree(GetProcessHeap(), 0, strA); } + + HeapFree(GetProcessHeap(), 0, formatted); + return count; }
-int __cdecl wmain(int argc, WCHAR *argv[]) +static BOOL get_user_name(EXTENDED_NAME_FORMAT name_format, WCHAR **name) { - WCHAR *buf = NULL; - ULONG size = 0; + ULONG size; BOOL result;
- if (argc > 1) + result = GetUserNameExW(name_format, NULL, &size); + if (result || GetLastError() != ERROR_MORE_DATA) { - int i; + ERR("GetUserNameExW expected ERROR_MORE_DATA, error %ld\n", GetLastError()); + return FALSE; + }
- WINE_FIXME("unsupported arguments:"); - for (i = 0; i < argc; i++) - WINE_FIXME(" %s", wine_dbgstr_w(argv[i])); - WINE_FIXME("\n"); + *name = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + if (!*name) + { + ERR("Memory allocation failed\n"); + return FALSE; }
- result = GetUserNameExW(NameSamCompatible, NULL, &size); - if (result || GetLastError() != ERROR_MORE_DATA) + result = GetUserNameExW(name_format, *name, &size); + if (!result) + { + ERR("GetUserNameExW failed, error %ld\n", GetLastError()); + return FALSE; + } + + return TRUE; +} + +static BOOL get_process_sid(PSID *sid) +{ + HANDLE token_handle; + DWORD buf[256]; /* GetTokenInformation wants a dword-aligned buffer */ + TOKEN_USER *token_user = (TOKEN_USER*)buf; + DWORD token_user_len; + BOOL err; + + err = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle); + if (!err) { - WINE_ERR("GetUserNameExW failed, result %d, error %ld\n", result, GetLastError()); + ERR("OpenProcessToken failed, error %ld\n", GetLastError()); + return FALSE; + } + + err = GetTokenInformation(token_handle, TokenUser, (void*)buf, + sizeof(buf), &token_user_len); + if (!err) + { + ERR("GetTokenInformation failed, error %ld\n", GetLastError()); + return FALSE; + } + + token_user_len = GetLengthSid(token_user->User.Sid); + *sid = HeapAlloc(GetProcessHeap(), 0, token_user_len); + if (!*sid) + { + ERR("Memory allocation failed\n"); + return FALSE; + } + + err = CopySid(token_user_len, *sid, token_user->User.Sid); + if (!err) + { + ERR("CopySid failed, error %ld\n", GetLastError()); + return FALSE; + } + + *sid = token_user->User.Sid; + + return TRUE; +} + +static BOOL get_logon_sid(PSID *sid) +{ + HANDLE token_handle; + DWORD buf[256]; /* GetTokenInformation wants a dword-aligned buffer */ + TOKEN_GROUPS *token_groups = (TOKEN_GROUPS*)buf; + DWORD token_groups_len; + BOOL err; + DWORD i; + + err = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle); + if (!err) + { + ERR("OpenProcessToken failed, error %ld\n", GetLastError()); + return FALSE; + } + + err = GetTokenInformation(token_handle, TokenGroups, (void*)buf, + sizeof(buf), &token_groups_len); + if (!err) + { + ERR("GetTokenInformation failed, error %ld\n", GetLastError()); + return FALSE; + } + + for (i = 0; i < token_groups->GroupCount; i++) + { + if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) + { + token_groups_len = GetLengthSid(token_groups->Groups[i].Sid); + *sid = HeapAlloc(GetProcessHeap(), 0, token_groups_len); + if (!*sid) + { + ERR("Memory allocation failed\n"); + return FALSE; + } + + err = CopySid(token_groups_len, *sid, token_groups->Groups[i].Sid); + if (!err) + { + ERR("CopySid failed, error %ld\n", GetLastError()); + return FALSE; + } + break; + } + } + + return TRUE; +} + +static int simple(EXTENDED_NAME_FORMAT name_format) +{ + WCHAR *name; + BOOL result; + + result = get_user_name(name_format, &name); + if (!result) + { + ERR("get_user_name failed\n"); return 1; }
- buf = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); - if (!buf) + unicode_wprintf(L"%ls\n", name); + + HeapFree(GetProcessHeap(), 0, name); + + return 0; +} + +static int logon_id(void) +{ + PSID sid; + BOOL status; + WCHAR *sid_string; + + status = get_logon_sid(&sid); + if (!status) { - WINE_ERR("Memory allocation failed\n"); + ERR("get_logon_sid failed\n"); return 1; }
- result = GetUserNameExW(NameSamCompatible, buf, &size); - if (result) + status = ConvertSidToStringSidW(sid, &sid_string); + if (!status) { - output_write(buf, size); - output_write(L"\r\n", 2); + ERR("ConvertSidToStringSidW failed, error %ld\n", GetLastError()); + return 1; } - else - WINE_ERR("GetUserNameExW failed, error %ld\n", GetLastError());
- HeapFree(GetProcessHeap(), 0, buf); + unicode_wprintf(L"%ls\n", sid_string); + + HeapFree(GetProcessHeap(), 0, sid); + LocalFree(sid_string); + + return 0; +} + +static int user(void) +{ + PSID sid; + BOOL status; + WCHAR *name; + WCHAR *sid_string; + + status = get_user_name(NameSamCompatible, &name); + if (!status) + { + ERR("get_user_name failed\n"); + return 1; + } + + status = get_process_sid(&sid); + if (!status) + { + ERR("get_process_sid failed\n"); + return 1; + } + + status = ConvertSidToStringSidW(sid, &sid_string); + if (!status) + { + ERR("ConvertSidToStringSidW failed, error %ld\n", GetLastError()); + return 1; + } + + /* TODO: prettify the table */ + unicode_wprintf(L"\n"); + unicode_wprintf(L"%ls\n", L"USER INFORMATION"); + unicode_wprintf(L"%ls\n", L"----------------"); + unicode_wprintf(L"\n"); + unicode_wprintf(L"%ls %ls\n", L"User Name ", L"SID"); + unicode_wprintf(L"%ls %ls\n", L"=========================", L"==============================="); + unicode_wprintf(L"%-25ls %ls\n", name, sid_string); + + HeapFree(GetProcessHeap(), 0, name); + HeapFree(GetProcessHeap(), 0, sid); + LocalFree(sid_string); + return 0; } + +int __cdecl wmain(int argc, WCHAR *argv[]) +{ + if (argv[1] == NULL) + { + + return simple(NameSamCompatible); + } + else + { + wcslwr(argv[1]); + + if (!wcscmp(argv[1], L"/upn")) + { + /* Not implemented as of now, therefore fails */ + return simple(NameUserPrincipal); + } + else if (!wcscmp(argv[1], L"/fqdn")) + { + /* Not implemented as of now, therefore fails */ + return simple(NameFullyQualifiedDN); + } + else if (!wcscmp(argv[1], L"/logonid")) + { + return logon_id(); + } + else if (!wcscmp(argv[1], L"/user")) + { + return user(); + } + else + { + FIXME("stub\n"); + return 0; + } + } +}