[PATCH v3 0/1] MR3347: klist: Add a program that lists Kerberos tickets.
It's a clean-room reimplementation that mimics Windows 10 program's output format. It prints all the information that is available via KerbQueryTicketCacheMessage. Also tested to work on Windows if dynamically linked + built with winegcc. For further extension of the functonality, implementing KerbQueryTicketCacheEx{,2,3}Message is required. -- v3: klist: Add a program that lists Kerberos tickets. https://gitlab.winehq.org/wine/wine/-/merge_requests/3347
From: Maxim Karasev <mxkrsv(a)etersoft.ru> It's a clean-room reimplementation that mimics Windows 10 program's output format. It prints all the information that is available via KerbQueryTicketCacheMessage. Also tested to work on Windows if dynamically linked + built with winegcc. For further extension of the functonality, implementing KerbQueryTicketCacheEx{,2,3}Message is required. --- configure | 2 + configure.ac | 1 + programs/klist/Makefile.in | 7 + programs/klist/main.c | 389 +++++++++++++++++++++++++++++++++++++ 4 files changed, 399 insertions(+) create mode 100644 programs/klist/Makefile.in create mode 100644 programs/klist/main.c diff --git a/configure b/configure index 4d3108303b8..be4790eb75f 100755 --- a/configure +++ b/configure @@ -1635,6 +1635,7 @@ enable_icacls enable_icinfo enable_iexplore enable_ipconfig +enable_klist enable_lodctr enable_mofcomp enable_mshta @@ -22175,6 +22176,7 @@ wine_fn_config_makefile programs/icacls enable_icacls wine_fn_config_makefile programs/icinfo enable_icinfo wine_fn_config_makefile programs/iexplore enable_iexplore wine_fn_config_makefile programs/ipconfig enable_ipconfig +wine_fn_config_makefile programs/klist enable_klist wine_fn_config_makefile programs/lodctr enable_lodctr wine_fn_config_makefile programs/mofcomp enable_mofcomp wine_fn_config_makefile programs/mshta enable_mshta diff --git a/configure.ac b/configure.ac index c9ed1c8c431..fc8a5ff03d5 100644 --- a/configure.ac +++ b/configure.ac @@ -3369,6 +3369,7 @@ WINE_CONFIG_MAKEFILE(programs/icacls) WINE_CONFIG_MAKEFILE(programs/icinfo) WINE_CONFIG_MAKEFILE(programs/iexplore) WINE_CONFIG_MAKEFILE(programs/ipconfig) +WINE_CONFIG_MAKEFILE(programs/klist) WINE_CONFIG_MAKEFILE(programs/lodctr) WINE_CONFIG_MAKEFILE(programs/mofcomp) WINE_CONFIG_MAKEFILE(programs/mshta) diff --git a/programs/klist/Makefile.in b/programs/klist/Makefile.in new file mode 100644 index 00000000000..cd0b644ca8d --- /dev/null +++ b/programs/klist/Makefile.in @@ -0,0 +1,7 @@ +MODULE = klist.exe +IMPORTS = secur32 advapi32 + +EXTRADLLFLAGS = -mconsole -municode + +C_SRCS = \ + main.c diff --git a/programs/klist/main.c b/programs/klist/main.c new file mode 100644 index 00000000000..a6f7fa4c210 --- /dev/null +++ b/programs/klist/main.c @@ -0,0 +1,389 @@ +/* + * Copyright 2023 Maxim Karasev <mxkrsv(a)etersoft.ru> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define SECURITY_WIN32 +#define UNICODE +#include <windows.h> +#include <security.h> +#include <ntsecapi.h> +#include <ntstatus.h> +#include <winnls.h> +#include <winnt.h> + +#include "wine/debug.h" + +#define USAGE L"Usage: klist <tickets | tgt | purge | get [service principal name]>\n" + +#define EXPAND_KERB_ETYPE(x) case KERB_ETYPE_##x: return L ## #x + +#define EXPAND_KERB_ETYPES(x) switch (x) { \ + EXPAND_KERB_ETYPE(NULL); \ + EXPAND_KERB_ETYPE(DES_CBC_CRC); \ + EXPAND_KERB_ETYPE(DES_CBC_MD4); \ + EXPAND_KERB_ETYPE(DES_CBC_MD5); \ + EXPAND_KERB_ETYPE(AES128_CTS_HMAC_SHA1_96); \ + EXPAND_KERB_ETYPE(AES256_CTS_HMAC_SHA1_96); \ + EXPAND_KERB_ETYPE(RC4_MD4); \ + EXPAND_KERB_ETYPE(RC4_PLAIN2); \ + EXPAND_KERB_ETYPE(RC4_LM); \ + EXPAND_KERB_ETYPE(RC4_SHA); \ + EXPAND_KERB_ETYPE(DES_PLAIN); \ + EXPAND_KERB_ETYPE(RC4_HMAC_OLD); \ + EXPAND_KERB_ETYPE(RC4_PLAIN_OLD); \ + EXPAND_KERB_ETYPE(RC4_HMAC_OLD_EXP); \ + EXPAND_KERB_ETYPE(RC4_PLAIN_OLD_EXP); \ + EXPAND_KERB_ETYPE(RC4_PLAIN); \ + EXPAND_KERB_ETYPE(RC4_PLAIN_EXP); \ + EXPAND_KERB_ETYPE(AES128_CTS_HMAC_SHA1_96_PLAIN); \ + EXPAND_KERB_ETYPE(AES256_CTS_HMAC_SHA1_96_PLAIN); \ + EXPAND_KERB_ETYPE(DSA_SHA1_CMS); \ + EXPAND_KERB_ETYPE(RSA_MD5_CMS); \ + EXPAND_KERB_ETYPE(RSA_SHA1_CMS); \ + EXPAND_KERB_ETYPE(RC2_CBC_ENV); \ + EXPAND_KERB_ETYPE(RSA_ENV); \ + EXPAND_KERB_ETYPE(RSA_ES_OEAP_ENV); \ + EXPAND_KERB_ETYPE(DES_EDE3_CBC_ENV); \ + EXPAND_KERB_ETYPE(DSA_SIGN); \ + EXPAND_KERB_ETYPE(DES3_CBC_MD5); \ + EXPAND_KERB_ETYPE(DES3_CBC_SHA1); \ + EXPAND_KERB_ETYPE(DES3_CBC_SHA1_KD); \ + EXPAND_KERB_ETYPE(DES_CBC_MD5_NT); \ + EXPAND_KERB_ETYPE(RC4_HMAC_NT); \ + EXPAND_KERB_ETYPE(RC4_HMAC_NT_EXP); \ + default: return NULL; } + +#define EXPAND_KERB_TICKET_FLAG(x, y) if (x & KERB_TICKET_FLAGS_##y) { wprintf(L" %ls", L#y); } + +#define EXPAND_KERB_TICKET_FLAGS(x) EXPAND_KERB_TICKET_FLAG(x, reserved) \ + EXPAND_KERB_TICKET_FLAG(x, forwardable) \ + EXPAND_KERB_TICKET_FLAG(x, forwarded) \ + EXPAND_KERB_TICKET_FLAG(x, proxiable) \ + EXPAND_KERB_TICKET_FLAG(x, proxy) \ + EXPAND_KERB_TICKET_FLAG(x, may_postdate) \ + EXPAND_KERB_TICKET_FLAG(x, postdated) \ + EXPAND_KERB_TICKET_FLAG(x, invalid) \ + EXPAND_KERB_TICKET_FLAG(x, renewable) \ + EXPAND_KERB_TICKET_FLAG(x, initial) \ + EXPAND_KERB_TICKET_FLAG(x, pre_authent) \ + EXPAND_KERB_TICKET_FLAG(x, hw_authent) \ + EXPAND_KERB_TICKET_FLAG(x, ok_as_delegate) \ + EXPAND_KERB_TICKET_FLAG(x, name_canonicalize) \ + EXPAND_KERB_TICKET_FLAG(x, cname_in_pa_data) \ + EXPAND_KERB_TICKET_FLAG(x, reserved1) + +WINE_DEFAULT_DEBUG_CHANNEL(klist); + +static WCHAR* get_etype_text(const LONG encryption_type) +{ + EXPAND_KERB_ETYPES(encryption_type) +} + +static BOOL get_process_logon_id(LUID *const logon_id) +{ + HANDLE token_handle; + TOKEN_STATISTICS token_statistics; + DWORD token_statistics_len; + BOOL err; + + err = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle); + if (!err) + { + WINE_ERR("OpenProcessToken failed\n"); + return err; + } + + err = GetTokenInformation(token_handle, TokenStatistics, &token_statistics, + sizeof(token_statistics), &token_statistics_len); + if (!err) + { + WINE_ERR("GetTokenInformation failed\n"); + return err; + } + + *logon_id = token_statistics.AuthenticationId; + + return TRUE; +} + +static BOOL format_dates_and_times(const FILETIME *const filetimes[], + const WCHAR *output_strings[], const ULONG count) +{ + ULONG i; + + for (i = 0; i < count; i++) + { + SYSTEMTIME system_time; + BOOL status; + int date_len; + int time_len; + WCHAR *date; + WCHAR *time; + + status = FileTimeToSystemTime(filetimes[i], &system_time); + if (!status) + { + WINE_ERR("FileTimeToSystemTime failed\n"); + return FALSE; + } + status = SystemTimeToTzSpecificLocalTime(NULL, &system_time, &system_time); + if (!status) + { + WINE_ERR("SystemTimeToTzSpecificLocalTime failed\n"); + return FALSE; + } + + date_len = GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &system_time, + NULL, NULL, 0, NULL); + if (date_len == 0) + { + WINE_ERR("GetDateFormatEx failed\n"); + return FALSE; + } + + time_len = GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &system_time, NULL, NULL, 0); + if (time_len == 0) + { + WINE_ERR("GetTimeFormatEx failed\n"); + return FALSE; + } + + date = HeapAlloc(GetProcessHeap(), 0, (date_len + time_len + 1) * sizeof(date[0])); + if (!date) + { + WINE_ERR("HeapAlloc failed\n"); + return FALSE; + } + + time = HeapAlloc(GetProcessHeap(), 0, time_len * sizeof(time[0])); + if (!time) + { + WINE_ERR("HeapAlloc failed\n"); + return FALSE; + } + + date_len = GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &system_time, + NULL, date, date_len, NULL); + if (date_len == 0) + { + WINE_ERR("GetDateFormatEx failed\n"); + return FALSE; + } + + time_len = GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &system_time, NULL, time, + time_len); + if (time_len == 0) + { + WINE_ERR("GetTimeFormatEx failed\n"); + return FALSE; + } + + wcscat(date, L" "); + wcscat(date, time); + + output_strings[i] = date; + } + + return TRUE; +} + +static int tickets(void) +{ + LUID logon_id; + BOOL status; + HANDLE lsa_handle; + NTSTATUS err; + ULONG i; + + LSA_STRING package_name = { + .Buffer = MICROSOFT_KERBEROS_NAME_A, + .Length = strlen(MICROSOFT_KERBEROS_NAME_A), + .MaximumLength = strlen(MICROSOFT_KERBEROS_NAME_A), + }; + ULONG kerberos_package; + + KERB_QUERY_TKT_CACHE_REQUEST kerberos_cache_request = { + .MessageType = KerbQueryTicketCacheMessage, + .LogonId = 0, + }; + KERB_QUERY_TKT_CACHE_RESPONSE *kerberos_cache; + ULONG kerberos_cache_size; + NTSTATUS kerberos_call_status; + + status = get_process_logon_id(&logon_id); + if (!status) + { + wprintf(L"Unknown error\n"); + WINE_ERR("get_process_logon_id failed\n"); + return 1; + } + + wprintf(L"\n"); + wprintf(L"Current LogonId is %ld:0x%lx\n", logon_id.HighPart, logon_id.LowPart); + wprintf(L"\n"); + + err = LsaConnectUntrusted(&lsa_handle); + if (err != STATUS_SUCCESS) + { + wprintf(L"Unknown error\n"); + WINE_ERR("LsaConnectUntrusted NTSTATUS %X\n", err); + return 1; + } + + err = LsaLookupAuthenticationPackage(lsa_handle, &package_name, &kerberos_package); + if (err != STATUS_SUCCESS) + { + wprintf(L"Unknown error\n"); + WINE_ERR("LsaLookupAuthenticationPackage NTSTATUS %X\n", err); + return 1; + } + + WINE_TRACE("Kerberos LSA package: %d\n", kerberos_package); + + err = LsaCallAuthenticationPackage(lsa_handle, kerberos_package, &kerberos_cache_request, + sizeof(kerberos_cache_request), (PVOID*)&kerberos_cache, &kerberos_cache_size, + &kerberos_call_status); + if (err != STATUS_SUCCESS) + { + wprintf(L"Unknown error\n"); + WINE_ERR("LsaCallAuthenticationPackage NTSTATUS %X\n", err); + return 1; + } + + wprintf(L"Cached Tickets: (%d)\n", kerberos_cache->CountOfTickets); + + for (i = 0; i < kerberos_cache->CountOfTickets; i++) + { + const KERB_TICKET_CACHE_INFO ticket = kerberos_cache->Tickets[i]; + const FILETIME *const filetimes[] = { (const FILETIME*)&ticket.StartTime, + (const FILETIME*)&ticket.EndTime, (const FILETIME*)&ticket.RenewTime }; + const WCHAR *dates[3]; + const WCHAR *etype_text; + + status = format_dates_and_times(filetimes, dates, 3); + if (!status) + { + wprintf(L"Unknown error\n"); + WINE_ERR("format_dates_and_times failed\n"); + return 1; + } + + wprintf(L"\n"); + wprintf(L"#%ld>", i); + + wprintf(L" Server: %.*ls @ %.*ls\n", + ticket.ServerName.Length / sizeof(WCHAR), ticket.ServerName.Buffer, + ticket.RealmName.Length / sizeof(WCHAR), ticket.RealmName.Buffer); + + etype_text = get_etype_text(ticket.EncryptionType); + wprintf(L" KerbTicket Encryption Type: "); + if (etype_text) + { + wprintf(L"%s\n", etype_text); + } + else + { + wprintf(L"%ld\n", ticket.EncryptionType); + } + + wprintf(L" Ticket Flags: 0x%lx ->", ticket.TicketFlags); + EXPAND_KERB_TICKET_FLAGS(ticket.TicketFlags) + wprintf(L"\n"); + + wprintf(L" Start Time: %ls (local)\n", dates[0]); + wprintf(L" End Time: %ls (local)\n", dates[1]); + wprintf(L" Renew Time: %ls (local)\n", dates[2]); + + for (int i = 0; i < 3; i++) + HeapFree(GetProcessHeap(), 0, (void*)dates[i]); + } + + err = LsaFreeReturnBuffer(kerberos_cache); + if (err != STATUS_SUCCESS) + { + wprintf(L"Unknown error\n"); + WINE_ERR("LsaFreeReturnBuffer NTSTATUS %X\n", err); + return 1; + } + + err = LsaDeregisterLogonProcess(lsa_handle); + if (err != STATUS_SUCCESS) + { + wprintf(L"Unknown error\n"); + WINE_ERR("LsaDeregisterLogonProcess NTSTATUS %X\n", err); + return 1; + } + + return 0; +} + +static int tgt(void) +{ + WINE_ERR("stub\n"); + + return 0; +} + +static int purge(void) +{ + WINE_ERR("stub\n"); + + return 0; +} + +static int get(const WCHAR *const principal) +{ + WINE_ERR("stub\n"); + + return 0; +} + +int __cdecl wmain(int argc, WCHAR *argv[]) +{ + if (argc < 2) + { + return tickets(); + } + + if (!wcscmp(argv[1], L"tickets")) + { + return tickets(); + } + else if (!wcscmp(argv[1], L"tgt")) + { + return tgt(); + } + else if (!wcscmp(argv[1], L"purge")) + { + return purge(); + } + else if (!wcscmp(argv[1], L"get")) + { + if (argc < 3) + { + wprintf(USAGE); + return 1; + } + + return get(argv[2]); + } + else + { + wprintf(USAGE); + return 1; + } +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3347
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=134936 Your paranoid android. === debian11 (build log) === ../wine/programs/klist/main.c:70:89: error: ���L��� undeclared (first use in this function) ../wine/programs/klist/main.c:304:52: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant Task: The win32 Wine build failed === debian11b (build log) === ../wine/programs/klist/main.c:70:89: error: ���L��� undeclared (first use in this function) ../wine/programs/klist/main.c:304:52: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant ../wine/programs/klist/main.c:304:1: error: expected ���)��� before string constant Task: The wow64 Wine build failed
participants (3)
-
Marvin -
Maxim Karasev -
Maxim Karasev (@mxkrsv)