Particularly it implements '/upn', '/logonid' and '/user' options, where the first one uses newly added NameUserPrincipal format in GetUserNameExW.
-- v2: whoami: Refactor and add more commands.
From: Maxim Karasev mxkrsv@etersoft.ru
--- dlls/secur32/secur32.c | 60 +++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 21 deletions(-)
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c index d703757bae7..beb52d7d6da 100644 --- a/dlls/secur32/secur32.c +++ b/dlls/secur32/secur32.c @@ -1108,31 +1108,49 @@ BOOLEAN WINAPI GetUserNameExW( { case NameSamCompatible: { - WCHAR samname[UNLEN + 1 + MAX_COMPUTERNAME_LENGTH + 1]; - LPWSTR out; - DWORD len; + WCHAR *output; + DWORD len = 0; + BOOL status;
- /* This assumes the current user is always a local account */ - len = MAX_COMPUTERNAME_LENGTH + 1; - if (GetComputerNameW(samname, &len)) + status = GetComputerNameExW(ComputerNameDnsHostname, NULL, &len); + if (status || GetLastError() != ERROR_MORE_DATA) { - out = samname + lstrlenW(samname); - *out++ = '\'; - len = UNLEN + 1; - if (GetUserNameW(out, &len)) - { - if (lstrlenW(samname) < *nSize) - { - lstrcpyW(lpNameBuffer, samname); - *nSize = lstrlenW(samname); - return TRUE; - } + ERR("GetComputerNameExW expected ERROR_MORE_DATA, error %lu\n", GetLastError()); + return FALSE; + }
- SetLastError(ERROR_MORE_DATA); - *nSize = lstrlenW(samname) + 1; - } + output = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (len + 1 + UNLEN + 1)); + + status = GetComputerNameExW(ComputerNameDnsHostname, output, &len); + if (!status) + { + ERR("GetComputerNameExW failed, error %lu\n", GetLastError()); + return FALSE; } - return FALSE; + + wcscat(output, L"\"); + + len = UNLEN + 1; + + status = GetUserNameW(&output[wcslen(output)], &len); + if (!status) + { + ERR("GetUserNameW failed, error %lu\n", GetLastError()); + return FALSE; + } + + if (lstrlenW(output) >= *nSize) + { + TRACE("Output buffer too small\n"); + SetLastError(ERROR_MORE_DATA); + *nSize = lstrlenW(output) + 1; + return FALSE; + } + + lstrcpyW(lpNameBuffer, output); + *nSize = lstrlenW(output); + + return TRUE; }
case NameUnknown:
From: Maxim Karasev mxkrsv@etersoft.ru
--- dlls/secur32/secur32.c | 47 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c index beb52d7d6da..1b1a91950bb 100644 --- a/dlls/secur32/secur32.c +++ b/dlls/secur32/secur32.c @@ -1153,12 +1153,57 @@ BOOLEAN WINAPI GetUserNameExW( return TRUE; }
+ case NameUserPrincipal: + { + WCHAR *output; + DWORD len = 0; + BOOL status; + DWORD username_len = UNLEN + 1; + + status = GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &len); + if (status || GetLastError() != ERROR_MORE_DATA) + { + ERR("GetComputerNameExW expected ERROR_MORE_DATA, error %lu\n", GetLastError()); + return FALSE; + } + + output = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (username_len + 1 + len)); + + status = GetUserNameW(output, &username_len); + if (!status) + { + ERR("GetUserNameW failed, error %lu\n", GetLastError()); + return FALSE; + } + + wcscat(output, L"@"); + + status = GetComputerNameExW(ComputerNameDnsFullyQualified, &output[wcslen(output)], &len); + if (!status) + { + ERR("GetComputerNameExW failed, error %lu\n", GetLastError()); + return FALSE; + } + + if (lstrlenW(output) >= *nSize) + { + TRACE("Output buffer too small\n"); + SetLastError(ERROR_MORE_DATA); + *nSize = lstrlenW(output) + 1; + return FALSE; + } + + lstrcpyW(lpNameBuffer, output); + *nSize = lstrlenW(output); + + return TRUE; + } + case NameUnknown: case NameFullyQualifiedDN: case NameDisplay: case NameUniqueId: case NameCanonical: - case NameUserPrincipal: case NameCanonicalEx: case NameServicePrincipal: case NameDnsDomain:
From: Maxim Karasev mxkrsv@etersoft.ru
Particularly it implements '/upn', '/logonid' and '/user' options, where the first one uses newly added NameUserPrincipal format in GetUserNameExW. --- 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..4f545bf38b4 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) + { + 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) { - WINE_ERR("GetUserNameExW failed, result %d, error %ld\n", result, GetLastError()); + 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")) + { + return simple(NameUserPrincipal); + } + else if (!wcscmp(argv[1], L"/logonid")) + { + return logon_id(); + } + else if (!wcscmp(argv[1], L"/user")) + { + return user(); + } + else if (!wcscmp(argv[1], L"/fqdn")) + { + /* Needs NameFullyQualifiedDN format in GetUserNameExW */ + FIXME("stub\n"); + return 0; + } + else + { + FIXME("stub\n"); + return 0; + } + } +}