22#include <freerdp/config.h>
24#include <winpr/windows.h>
25#include <winpr/library.h>
28#include <winpr/assert.h>
35#include <winpr/assert.h>
39#ifdef WITH_PROGRESS_BAR
43#ifdef WITH_WINDOWS_CERT_STORE
47#include <freerdp/log.h>
48#include <freerdp/freerdp.h>
49#include <freerdp/constants.h>
50#include <freerdp/settings.h>
52#include <freerdp/locale/locale.h>
53#include <freerdp/locale/keyboard.h>
54#include <freerdp/codec/region.h>
55#include <freerdp/client.h>
56#include <freerdp/client/cmdline.h>
57#include <freerdp/client/channels.h>
58#include <freerdp/client/aad_helper.h>
59#include <freerdp/channels/channels.h>
60#include <freerdp/utils/signal.h>
64#include "wf_channels.h"
65#include "wf_graphics.h"
67#include "resource/resource.h"
69#define TAG CLIENT_TAG("windows")
71#define WM_FREERDP_SHOWWINDOW (WM_USER + 100)
73static BOOL wf_has_console(
void)
75#ifdef WITH_WIN_CONSOLE
76 int file = _fileno(stdin);
77 int tty = _isatty(file);
78 DWORD processes[2] = WINPR_C_ARRAY_INIT;
79 const DWORD count = GetConsoleProcessList(processes, ARRAYSIZE(processes));
80 const BOOL inherited = (count > 1);
84 BOOL inherited = FALSE;
87 WLog_INFO(TAG,
"Detected stdin=%d -> %s mode", file, (tty && inherited) ?
"console" :
"gui");
88 return tty && inherited;
91static BOOL wf_end_paint(rdpContext* context)
93 RECT updateRect = WINPR_C_ARRAY_INIT;
94 REGION16 invalidRegion = WINPR_C_ARRAY_INIT;
98 WINPR_ASSERT(context);
100 wfContext* wfc = (wfContext*)context;
101 rdpGdi* gdi = context->gdi;
104 HGDI_DC hdc = gdi->primary->hdc;
110 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
112 if (hwnd->invalid->null)
115 const int ninvalid = hwnd->ninvalid;
116 const HGDI_RGN cinvalid = hwnd->cinvalid;
121 region16_init(&invalidRegion);
123 for (
int i = 0; i < ninvalid; i++)
125 invalidRect.left = cinvalid[i].x;
126 invalidRect.top = cinvalid[i].y;
127 invalidRect.right = cinvalid[i].x + cinvalid[i].w;
128 invalidRect.bottom = cinvalid[i].y + cinvalid[i].h;
129 region16_union_rect(&invalidRegion, &invalidRegion, &invalidRect);
132 if (!region16_is_empty(&invalidRegion))
134 extents = region16_extents(&invalidRegion);
135 updateRect.left = extents->left;
136 updateRect.top = extents->top;
137 updateRect.right = extents->right;
138 updateRect.bottom = extents->bottom;
140 wf_scale_rect(wfc, &updateRect);
142 InvalidateRect(wfc->hwnd, &updateRect, FALSE);
145 wf_rail_invalidate_region(wfc, &invalidRegion);
148 region16_uninit(&invalidRegion);
152 wfc->is_shown = TRUE;
154#ifdef WITH_PROGRESS_BAR
155 if (wfc->taskBarList)
157 wfc->taskBarList->lpVtbl->SetProgressState(wfc->taskBarList, wfc->hwnd,
162 PostMessage(wfc->hwnd, WM_FREERDP_SHOWWINDOW, 0, 0);
163 WLog_INFO(TAG,
"Window is shown!");
168static BOOL wf_begin_paint(rdpContext* context)
172 if (!context || !context->gdi || !context->gdi->primary || !context->gdi->primary->hdc)
175 hdc = context->gdi->primary->hdc;
177 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
180 hdc->hwnd->invalid->null = TRUE;
181 hdc->hwnd->ninvalid = 0;
185static BOOL wf_desktop_resize(rdpContext* context)
189 rdpSettings* settings;
190 wfContext* wfc = (wfContext*)context;
192 if (!context || !context->settings)
195 settings = context->settings;
199 same = (wfc->primary == wfc->drawing) ? TRUE : FALSE;
200 wf_image_free(wfc->primary);
204 context->gdi->dstFormat,
nullptr);
209 context->gdi->dstFormat, wfc->primary->pdata,
nullptr))
213 wfc->drawing = wfc->primary;
215 if (wfc->fullscreen != TRUE)
218 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
225 wf_update_offset(wfc);
226 GetWindowRect(wfc->hwnd, &rect);
227 InvalidateRect(wfc->hwnd, &rect, TRUE);
233static BOOL wf_pre_connect(freerdp* instance)
235 WINPR_ASSERT(instance);
236 WINPR_ASSERT(instance->context);
237 WINPR_ASSERT(instance->context->settings);
239 rdpContext* context = instance->context;
240 wfContext* wfc = (wfContext*)instance->context;
241 rdpSettings* settings = context->settings;
251 if (wfc->percentscreen > 0)
253 desktopWidth = (GetSystemMetrics(SM_CXSCREEN) * wfc->percentscreen) / 100;
256 desktopHeight = (GetSystemMetrics(SM_CYSCREEN) * wfc->percentscreen) / 100;
265 desktopWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
266 desktopHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
270 desktopWidth = GetSystemMetrics(SM_CXSCREEN);
271 desktopHeight = GetSystemMetrics(SM_CYSCREEN);
277 desktopWidth = (desktopWidth + 3) & (~3);
294 CHAR name[KL_NAMELENGTH + 1] = WINPR_C_ARRAY_INIT;
295 if (GetKeyboardLayoutNameA(name))
300 rc = strtoul(name,
nullptr, 16);
302 keyboardLayoutId = rc;
305 if (keyboardLayoutId == 0)
307 const HKL layout = GetKeyboardLayout(0);
308 const uint32_t masked = (uint32_t)(((uintptr_t)layout >> 16) & 0xFFFF);
309 keyboardLayoutId = masked;
313 if (keyboardLayoutId == 0)
314 freerdp_detect_keyboard_layout_from_system_locale(&keyboardLayoutId);
315 if (keyboardLayoutId == 0)
316 keyboardLayoutId = ENGLISH_UNITED_STATES;
319 PubSub_SubscribeChannelConnected(instance->context->pubSub, wf_OnChannelConnectedEventHandler);
320 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
321 wf_OnChannelDisconnectedEventHandler);
325static void wf_append_item_to_system_menu(HMENU hMenu, UINT fMask, UINT wID,
const wchar_t* text,
328 MENUITEMINFO item_info = WINPR_C_ARRAY_INIT;
329 item_info.fMask = fMask;
330 item_info.cbSize =
sizeof(MENUITEMINFO);
332 item_info.fType = MFT_STRING;
333 item_info.dwTypeData = _wcsdup(text);
334 item_info.cch = (UINT)_wcslen(text);
336 item_info.dwItemData = (ULONG_PTR)wfc;
337 InsertMenuItem(hMenu, wfc->systemMenuInsertPosition++, TRUE, &item_info);
340static void wf_add_system_menu(wfContext* wfc)
344 if (wfc->fullscreen && !wfc->fullscreen_toggle)
354 hMenu = GetSystemMenu(wfc->hwnd, FALSE);
356 wf_append_item_to_system_menu(hMenu,
357 MIIM_CHECKMARKS | MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_DATA,
358 SYSCOMMAND_ID_SMARTSIZING, L
"Smart sizing", wfc);
362 CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING, MF_CHECKED);
366 wf_append_item_to_system_menu(hMenu, MIIM_FTYPE | MIIM_ID | MIIM_STRING,
367 SYSCOMMAND_ID_REQUEST_CONTROL, L
"Request control", wfc);
370static WCHAR* wf_window_get_title(rdpSettings* settings)
373 WCHAR* windowTitle =
nullptr;
375 WCHAR prefix[] = L
"FreeRDP:";
387 size = strlen(name) + 16 + wcslen(prefix);
388 windowTitle = calloc(size,
sizeof(WCHAR));
394 _snwprintf_s(windowTitle, size, _TRUNCATE, L
"%s %S", prefix, name);
396 _snwprintf_s(windowTitle, size, _TRUNCATE, L
"%s %S:%u", prefix, name,
402static BOOL wf_post_connect(freerdp* instance)
409 rdpSettings* settings;
410 EmbedWindowEventArgs e;
411 const UINT32 format = PIXEL_FORMAT_BGRX32;
413 WINPR_ASSERT(instance);
415 context = instance->context;
416 WINPR_ASSERT(context);
418 settings = context->settings;
419 WINPR_ASSERT(settings);
421 wfc = (wfContext*)instance->context;
430 WLog_ERR(TAG,
"Failed to allocate primary surface");
434 if (!gdi_init_ex(instance, format, 0, wfc->primary->pdata,
nullptr))
437 cache = instance->context->cache;
440 gdi = instance->context->gdi;
444 wf_gdi_register_update_callbacks(context->update);
447 wfc->window_title = wf_window_get_title(settings);
449 if (!wfc->window_title)
461 dwStyle = WS_CHILD | WS_BORDER;
464 WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX;
468 wfc->hwnd = CreateWindowEx(0, wfc->wndClassName, wfc->window_title, dwStyle, 0, 0, 0, 0,
469 wfc->hWndParent,
nullptr, wfc->hInstance,
nullptr);
472 WLog_ERR(TAG,
"CreateWindowEx failed with error: %lu", GetLastError());
475 SetWindowLongPtr(wfc->hwnd, GWLP_USERDATA, (LONG_PTR)wfc);
478 wf_resize_window(wfc);
479 wf_add_system_menu(wfc);
482 wfc->drawing = wfc->primary;
483 EventArgsInit(&e,
"wfreerdp");
485 e.handle = (
void*)wfc->hwnd;
486 if (PubSub_OnEmbedWindow(context->pubSub, context, &e) < 0)
489#ifdef WITH_PROGRESS_BAR
490 if (wfc->taskBarList)
492 ShowWindow(wfc->hwnd, SW_SHOWMINIMIZED);
493 wfc->taskBarList->lpVtbl->SetProgressState(wfc->taskBarList, wfc->hwnd, TBPF_INDETERMINATE);
496 UpdateWindow(wfc->hwnd);
497 context->update->BeginPaint = wf_begin_paint;
498 context->update->DesktopResize = wf_desktop_resize;
499 context->update->EndPaint = wf_end_paint;
500 context->update->SetKeyboardIndicators = wf_keyboard_set_indicators;
501 wf_register_pointer(context->graphics);
503 wfc->floatbar = wf_floatbar_new(wfc, wfc->hInstance,
506 wf_event_focus_in(wfc);
511static void wf_post_disconnect(freerdp* instance)
515 if (!instance || !instance->context)
518 wfc = (wfContext*)instance->context;
519 free(wfc->window_title);
522static CREDUI_INFOW wfUiInfo = {
sizeof(CREDUI_INFOW),
nullptr, L
"Enter your credentials",
523 L
"Remote Desktop Security",
nullptr };
525static BOOL wf_authenticate_ex(freerdp* instance,
char** username,
char** password,
char** domain,
526 rdp_auth_reason reason)
532 WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = WINPR_C_ARRAY_INIT;
533 WCHAR UserW[CREDUI_MAX_USERNAME_LENGTH + 1] = WINPR_C_ARRAY_INIT;
534 WCHAR DomainW[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = WINPR_C_ARRAY_INIT;
535 WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = WINPR_C_ARRAY_INIT;
537 WINPR_ASSERT(instance);
538 WINPR_ASSERT(instance->context);
539 WINPR_ASSERT(instance->context->settings);
541 wfc = (wfContext*)instance->context;
544 WINPR_ASSERT(username);
545 WINPR_ASSERT(domain);
546 WINPR_ASSERT(password);
552 return client_cli_authenticate_ex(instance, username, password, domain, reason);
554 const WCHAR auth[] = L
"Target credentials requested";
555 const WCHAR authPin[] = L
"PIN requested";
556 const WCHAR gwAuth[] = L
"Gateway credentials requested";
557 const WCHAR* titleW = auth;
560 dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
561 CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
568 if ((*username) && (*password))
571 case AUTH_SMARTCARD_PIN:
572 dwFlags &= ~CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
573 dwFlags |= CREDUI_FLAGS_PASSWORD_ONLY_OK | CREDUI_FLAGS_KEEP_USERNAME;
578 *username = _strdup(
"PIN");
591 (void)ConvertUtf8ToWChar(*username, UserNameW, ARRAYSIZE(UserNameW));
592 (void)ConvertUtf8ToWChar(*username, UserW, ARRAYSIZE(UserW));
596 (void)ConvertUtf8ToWChar(*password, PasswordW, ARRAYSIZE(PasswordW));
599 (void)ConvertUtf8ToWChar(*domain, DomainW, ARRAYSIZE(DomainW));
601 if (_wcsnlen(PasswordW, ARRAYSIZE(PasswordW)) == 0)
603 if (!wfc->isConsole &&
605 WLog_ERR(TAG,
"Flag for stdin read present but stdin is redirected; using GUI");
606 if (wfc->isConsole &&
608 status = CredUICmdLinePromptForCredentialsW(titleW,
nullptr, 0, UserNameW,
609 ARRAYSIZE(UserNameW), PasswordW,
610 ARRAYSIZE(PasswordW), &fSave, dwFlags);
612 status = CredUIPromptForCredentialsW(&wfUiInfo, titleW,
nullptr, 0, UserNameW,
613 ARRAYSIZE(UserNameW), PasswordW,
614 ARRAYSIZE(PasswordW), &fSave, dwFlags);
615 if (status != NO_ERROR)
617 WLog_ERR(TAG,
"CredUIPromptForCredentials unexpected status: 0x%08lX", status);
621 if ((dwFlags & CREDUI_FLAGS_KEEP_USERNAME) == 0)
623 status = CredUIParseUserNameW(UserNameW, UserW, ARRAYSIZE(UserW), DomainW,
625 if (status != NO_ERROR)
627 CHAR User[CREDUI_MAX_USERNAME_LENGTH + 1] = WINPR_C_ARRAY_INIT;
628 CHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1] = WINPR_C_ARRAY_INIT;
629 CHAR Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = WINPR_C_ARRAY_INIT;
631 (void)ConvertWCharNToUtf8(UserNameW, ARRAYSIZE(UserNameW), UserName,
632 ARRAYSIZE(UserName));
633 (void)ConvertWCharNToUtf8(UserW, ARRAYSIZE(UserW), User, ARRAYSIZE(User));
634 (void)ConvertWCharNToUtf8(DomainW, ARRAYSIZE(DomainW), Domain, ARRAYSIZE(Domain));
635 WLog_ERR(TAG,
"Failed to parse UserName: %s into User: %s Domain: %s", UserName,
642 *username = ConvertWCharNToUtf8Alloc(UserW, ARRAYSIZE(UserW),
nullptr);
645 WLog_ERR(TAG,
"ConvertWCharNToUtf8Alloc failed", status);
649 if (_wcsnlen(DomainW, ARRAYSIZE(DomainW)) > 0)
650 *domain = ConvertWCharNToUtf8Alloc(DomainW, ARRAYSIZE(DomainW),
nullptr);
652 *domain = _strdup(
"\0");
657 WLog_ERR(TAG,
"strdup failed", status);
661 *password = ConvertWCharNToUtf8Alloc(PasswordW, ARRAYSIZE(PasswordW),
nullptr);
672static WCHAR* wf_format_text(
const WCHAR* fmt, ...)
676 WCHAR* buffer =
nullptr;
680 WCHAR* tmp =
nullptr;
681 va_list ap = WINPR_C_ARRAY_INIT;
683 rc = _vsnwprintf(buffer, size, fmt, ap);
688 if ((
size_t)rc < size)
691 size = (size_t)rc + 1;
692 tmp = realloc(buffer, size *
sizeof(WCHAR));
704#ifdef WITH_WINDOWS_CERT_STORE
712static void wf_report_error(
char* wszMessage, DWORD dwErrCode)
714 LPSTR pwszMsgBuf =
nullptr;
716 if (
nullptr != wszMessage && 0 != *wszMessage)
718 WLog_ERR(TAG,
"%s", wszMessage);
721 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
726 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
736 if (
nullptr != pwszMsgBuf)
738 WLog_ERR(TAG,
"Error: 0x%08x (%d) %s", dwErrCode, dwErrCode, pwszMsgBuf);
739 LocalFree(pwszMsgBuf);
743 WLog_ERR(TAG,
"Error: 0x%08x (%d)", dwErrCode, dwErrCode);
747static DWORD wf_is_x509_certificate_trusted(
const char* common_name,
const char* subject,
748 const char* issuer,
const char* fingerprint)
750 HRESULT hr = CRYPT_E_NOT_FOUND;
752 DWORD dwChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
754 HCERTCHAINENGINE hChainEngine =
nullptr;
755 PCCERT_CHAIN_CONTEXT pChainContext =
nullptr;
757 CERT_ENHKEY_USAGE EnhkeyUsage = WINPR_C_ARRAY_INIT;
758 CERT_USAGE_MATCH CertUsage = WINPR_C_ARRAY_INIT;
759 CERT_CHAIN_PARA ChainPara = WINPR_C_ARRAY_INIT;
760 CERT_CHAIN_POLICY_PARA ChainPolicy = WINPR_C_ARRAY_INIT;
761 CERT_CHAIN_POLICY_STATUS PolicyStatus = WINPR_C_ARRAY_INIT;
762 CERT_CHAIN_ENGINE_CONFIG EngineConfig = WINPR_C_ARRAY_INIT;
764 DWORD derPubKeyLen = WINPR_ASSERTING_INT_CAST(uint32_t, strlen(fingerprint));
765 char* derPubKey = calloc(derPubKeyLen,
sizeof(
char));
766 if (
nullptr == derPubKey)
768 WLog_ERR(TAG,
"Could not allocate derPubKey");
775 if (!CryptStringToBinaryA(fingerprint, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen,
778 WLog_ERR(TAG,
"CryptStringToBinary failed. Err: %d", GetLastError());
785 EnhkeyUsage.cUsageIdentifier = 0;
786 EnhkeyUsage.rgpszUsageIdentifier =
nullptr;
788 CertUsage.dwType = USAGE_MATCH_TYPE_AND;
789 CertUsage.Usage = EnhkeyUsage;
791 ChainPara.cbSize =
sizeof(ChainPara);
792 ChainPara.RequestedUsage = CertUsage;
794 ChainPolicy.cbSize =
sizeof(ChainPolicy);
796 PolicyStatus.cbSize =
sizeof(PolicyStatus);
798 EngineConfig.cbSize =
sizeof(EngineConfig);
799 EngineConfig.dwUrlRetrievalTimeout = 0;
801 pCert = CertCreateCertificateContext(X509_ASN_ENCODING, derPubKey, derPubKeyLen);
802 if (
nullptr == pCert)
804 WLog_ERR(TAG,
"FAILED: Certificate could not be parsed.");
808 dwChainFlags |= CERT_CHAIN_ENABLE_PEER_TRUST;
817 if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
819 hr = HRESULT_FROM_WIN32(GetLastError());
826 if (!CertGetCertificateChain(hChainEngine,
837 hr = HRESULT_FROM_WIN32(GetLastError());
844 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE,
849 hr = HRESULT_FROM_WIN32(GetLastError());
853 if (PolicyStatus.dwError != S_OK)
855 wf_report_error(
"CertVerifyCertificateChainPolicy: Chain Status", PolicyStatus.dwError);
856 hr = PolicyStatus.dwError;
862 if (PolicyStatus.dwError == CRYPT_E_NO_REVOCATION_CHECK ||
863 PolicyStatus.dwError == CRYPT_E_REVOCATION_OFFLINE)
871 WLog_INFO(TAG,
"CertVerifyCertificateChainPolicy succeeded for %s (%s) issued by %s",
872 common_name, subject, issuer);
879 WLog_INFO(TAG,
"CertVerifyCertificateChainPolicy failed for %s (%s) issued by %s",
880 common_name, subject, issuer);
881 wf_report_error(
nullptr, hr);
886 if (
nullptr != pChainContext)
888 CertFreeCertificateChain(pChainContext);
891 if (
nullptr != hChainEngine)
893 CertFreeCertificateChainEngine(hChainEngine);
896 if (
nullptr != pCert)
898 CertFreeCertificateContext(pCert);
905static DWORD wf_cli_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
906 const char* common_name,
const char* subject,
907 const char* issuer,
const char* fingerprint, DWORD flags)
909#ifdef WITH_WINDOWS_CERT_STORE
910 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
912 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
919 return client_cli_verify_certificate_ex(instance, host, port, common_name, subject, issuer,
923static DWORD wf_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
924 const char* common_name,
const char* subject,
925 const char* issuer,
const char* fingerprint, DWORD flags)
931#ifdef WITH_WINDOWS_CERT_STORE
932 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
934 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
941 buffer = wf_format_text(
942 L
"Certificate details:\n"
943 L
"\tCommonName: %S\n"
946 L
"\tThumbprint: %S\n"
947 L
"\tHostMismatch: %S\n"
949 L
"The above X.509 certificate could not be verified, possibly because you do not have "
950 L
"the CA certificate in your certificate store, or the certificate has expired. "
951 L
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n"
953 L
"YES\tAccept permanently\n"
954 L
"NO\tAccept for this session only\n"
955 L
"CANCEL\tAbort connection\n",
956 common_name, subject, issuer, fingerprint,
957 flags & VERIFY_CERT_FLAG_MISMATCH ?
"Yes" :
"No");
958 caption = wf_format_text(L
"Verify certificate for %S:%hu", host, port);
960 WINPR_UNUSED(instance);
962 if (!buffer || !caption)
965 what = MessageBoxW(
nullptr, buffer, caption, MB_YESNOCANCEL);
983static DWORD wf_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
984 const char* common_name,
const char* subject,
985 const char* issuer,
const char* new_fingerprint,
986 const char* old_subject,
const char* old_issuer,
987 const char* old_fingerprint, DWORD flags)
993 buffer = wf_format_text(
994 L
"New Certificate details:\n"
995 L
"\tCommonName: %S\n"
998 L
"\tThumbprint: %S\n"
999 L
"\tHostMismatch: %S\n"
1001 L
"Old Certificate details:\n"
1005 L
"The above X.509 certificate could not be verified, possibly because you do not have "
1006 L
"the CA certificate in your certificate store, or the certificate has expired. "
1007 L
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n"
1009 L
"YES\tAccept permanently\n"
1010 L
"NO\tAccept for this session only\n"
1011 L
"CANCEL\tAbort connection\n",
1012 common_name, subject, issuer, new_fingerprint,
1013 flags & VERIFY_CERT_FLAG_MISMATCH ?
"Yes" :
"No", old_subject, old_issuer, old_fingerprint);
1014 caption = wf_format_text(L
"Verify certificate change for %S:%hu", host, port);
1016 WINPR_UNUSED(instance);
1017 if (!buffer || !caption)
1020 what = MessageBoxW(
nullptr, buffer, caption, MB_YESNOCANCEL);
1038static BOOL wf_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1039 BOOL isConsentMandatory,
size_t length,
const WCHAR* message)
1041 if (!isDisplayMandatory && !isConsentMandatory)
1045 if (type == GATEWAY_MESSAGE_CONSENT && isConsentMandatory)
1050 msg = wf_format_text(L
"%.*s\n\nI understand and agree to the terms of this policy", length,
1052 mbRes = MessageBoxW(
nullptr, msg, L
"Consent Message", MB_YESNO);
1059 return client_cli_present_gateway_message(instance, type, isDisplayMandatory,
1060 isConsentMandatory, length, message);
1065static DWORD WINAPI wf_client_thread(LPVOID lpParam)
1067 MSG msg = WINPR_C_ARRAY_INIT;
1070 BOOL msg_ret = FALSE;
1074 freerdp* instance = (freerdp*)lpParam;
1075 WINPR_ASSERT(instance);
1077 if (!freerdp_connect(instance))
1080 rdpContext* context = instance->context;
1081 WINPR_ASSERT(context);
1083 wfContext* wfc = (wfContext*)instance->context;
1086 rdpChannels* channels = context->channels;
1087 WINPR_ASSERT(channels);
1089 rdpSettings* settings = context->settings;
1090 WINPR_ASSERT(settings);
1092 while (!freerdp_shall_disconnect_context(instance->context))
1094 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
1097 if (freerdp_focus_required(instance))
1099 wf_event_focus_in(wfc);
1100 wf_event_focus_in(wfc);
1104 DWORD tmp = freerdp_get_event_handles(context, &handles[nCount], 64 - nCount);
1108 WLog_ERR(TAG,
"freerdp_get_event_handles failed");
1115 DWORD status = MsgWaitForMultipleObjectsEx(nCount, handles, 5 * 1000, QS_ALLINPUT,
1116 MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
1117 if (status == WAIT_FAILED)
1119 WLog_ERR(TAG,
"wfreerdp_run: WaitForMultipleObjects failed: 0x%08lX", GetLastError());
1124 if (!freerdp_check_event_handles(context))
1126 if (client_auto_reconnect(instance))
1129 WLog_ERR(TAG,
"Failed to check FreeRDP file descriptor");
1134 if (freerdp_shall_disconnect_context(instance->context))
1139 while (PeekMessage(&msg,
nullptr, 0, 0, PM_NOREMOVE))
1141 msg_ret = GetMessage(&msg,
nullptr, 0, 0);
1145 if ((msg.message == WM_SETFOCUS) && (msg.lParam == 1))
1147 PostMessage(wfc->hwnd, WM_SETFOCUS, 0, 0);
1149 else if ((msg.message == WM_KILLFOCUS) && (msg.lParam == 1))
1151 PostMessage(wfc->hwnd, WM_KILLFOCUS, 0, 0);
1155 switch (msg.message)
1159 width = LOWORD(msg.lParam);
1160 height = HIWORD(msg.lParam);
1161 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED);
1164 case WM_FREERDP_SHOWWINDOW:
1166 ShowWindow(wfc->hwnd, SW_NORMAL);
1169 wf_floatbar_reset_position(wfc->floatbar);
1177 if ((msg_ret == 0) || (msg_ret == -1))
1183 TranslateMessage(&msg);
1184 DispatchMessage(&msg);
1192 freerdp_disconnect(instance);
1195 error = freerdp_get_last_error(instance->context);
1196 WLog_DBG(TAG,
"Main thread exited with %" PRIu32, error);
1201static DWORD WINAPI wf_keyboard_thread(LPVOID lpParam)
1207 wfc = (wfContext*)lpParam;
1208 WINPR_ASSERT(
nullptr != wfc);
1209 hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, wf_ll_kbd_proc, wfc->hInstance, 0);
1213 while ((status = GetMessage(&msg,
nullptr, 0, 0)) != 0)
1217 WLog_ERR(TAG,
"keyboard thread error getting message");
1222 TranslateMessage(&msg);
1223 DispatchMessage(&msg);
1227 UnhookWindowsHookEx(hook_handle);
1231 WLog_ERR(TAG,
"failed to install keyboard hook");
1234 WLog_DBG(TAG,
"Keyboard thread exited.");
1239int freerdp_client_set_window_size(wfContext* wfc,
int width,
int height)
1241 WLog_DBG(TAG,
"freerdp_client_set_window_size %d, %d", width, height);
1243 if ((width != wfc->client_width) || (height != wfc->client_height))
1245 PostThreadMessage(wfc->mainThreadId, WM_SIZE, SIZE_RESTORED,
1246 ((UINT)height << 16) | (UINT)width);
1252void wf_size_scrollbars(wfContext* wfc, UINT32 client_width, UINT32 client_height)
1254 const rdpSettings* settings;
1257 settings = wfc->common.context.settings;
1258 WINPR_ASSERT(settings);
1260 if (wfc->disablewindowtracking)
1264 wfc->disablewindowtracking = TRUE;
1269 wfc->xCurrentScroll = 0;
1270 wfc->yCurrentScroll = 0;
1272 if (wfc->xScrollVisible || wfc->yScrollVisible)
1274 if (ShowScrollBar(wfc->hwnd, SB_BOTH, FALSE))
1276 wfc->xScrollVisible = FALSE;
1277 wfc->yScrollVisible = FALSE;
1284 BOOL horiz = wfc->xScrollVisible;
1285 BOOL vert = wfc->yScrollVisible;
1286 BOOL redraw = FALSE;
1295 settings, FreeRDP_DesktopWidth) )
1307 settings, FreeRDP_DesktopHeight) )
1312 if (horiz == vert && (horiz != wfc->xScrollVisible && vert != wfc->yScrollVisible))
1314 if (ShowScrollBar(wfc->hwnd, SB_BOTH, horiz))
1316 wfc->xScrollVisible = horiz;
1317 wfc->yScrollVisible = vert;
1321 if (horiz != wfc->xScrollVisible)
1323 if (ShowScrollBar(wfc->hwnd, SB_HORZ, horiz))
1325 wfc->xScrollVisible = horiz;
1329 if (vert != wfc->yScrollVisible)
1331 if (ShowScrollBar(wfc->hwnd, SB_VERT, vert))
1333 wfc->yScrollVisible = vert;
1344 wfc->xCurrentScroll = MIN(wfc->xCurrentScroll, wfc->xMaxScroll);
1345 si.cbSize =
sizeof(si);
1346 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1347 si.nMin = wfc->xMinScroll;
1349 si.nPage = client_width;
1350 si.nPos = wfc->xCurrentScroll;
1351 SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
1355 if (wfc->xCurrentScroll != 0)
1357 wfc->xCurrentScroll = 0;
1367 wfc->yMaxScroll = MAX(
1369 wfc->yCurrentScroll = MIN(wfc->yCurrentScroll, wfc->yMaxScroll);
1370 si.cbSize =
sizeof(si);
1371 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1372 si.nMin = wfc->yMinScroll;
1374 si.nPage = client_height;
1375 si.nPos = wfc->yCurrentScroll;
1376 SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
1380 if (wfc->yCurrentScroll != 0)
1382 wfc->yCurrentScroll = 0;
1389 InvalidateRect(wfc->hwnd,
nullptr, FALSE);
1390 UpdateWindow(wfc->hwnd);
1394 wfc->disablewindowtracking = FALSE;
1395 wf_update_canvas_diff(wfc);
1398static BOOL wfreerdp_client_global_init(
void)
1402 WSAStartup(0x101, &wsaData);
1403 if (freerdp_handle_signals() != 0)
1406 if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
1413static void wfreerdp_client_global_uninit(
void)
1418static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context)
1420 wfContext* wfc = (wfContext*)context;
1426 wfc->isConsole = wf_has_console();
1428#ifdef WITH_WIN_CONSOLE
1429 if (!wfc->isConsole)
1431 HWND hwndConsole = GetConsoleWindow();
1433 ShowWindow(hwndConsole, SW_HIDE);
1434 (void)FreeConsole();
1438 if (!(wfreerdp_client_global_init()))
1441 WINPR_ASSERT(instance);
1442 instance->PreConnect = wf_pre_connect;
1443 instance->PostConnect = wf_post_connect;
1444 instance->PostDisconnect = wf_post_disconnect;
1445 instance->AuthenticateEx = wf_authenticate_ex;
1446 instance->GetAccessToken = client_failsafe_get_access_token;
1448#ifdef WITH_WINDOWS_CERT_STORE
1453 if (!wfc->isConsole)
1455 instance->VerifyCertificateEx = wf_verify_certificate_ex;
1456 instance->VerifyChangedCertificateEx = wf_verify_changed_certificate_ex;
1457 instance->PresentGatewayMessage = wf_present_gateway_message;
1460#ifdef WITH_PROGRESS_BAR
1461 CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
1462 CoCreateInstance(&CLSID_TaskbarList,
nullptr, CLSCTX_ALL, &IID_ITaskbarList3,
1463 (
void**)&wfc->taskBarList);
1469static void wfreerdp_client_free(freerdp* instance, rdpContext* context)
1471 WINPR_UNUSED(instance);
1475#ifdef WITH_PROGRESS_BAR
1480static int wfreerdp_client_start(rdpContext* context)
1482 wfContext* wfc = (wfContext*)context;
1484 WINPR_ASSERT(context);
1485 WINPR_ASSERT(context->settings);
1487 freerdp* instance = context->instance;
1488 WINPR_ASSERT(instance);
1490 rdpSettings* settings = context->settings;
1491 WINPR_ASSERT(settings);
1493 HINSTANCE hInstance = GetModuleHandle(
nullptr);
1498 wfc->hWndParent = hWndParent;
1502 typedef UINT(WINAPI * GetDpiForWindow_t)(HWND hwnd);
1503 typedef BOOL(WINAPI * SetProcessDPIAware_t)(void);
1505 HMODULE
module = GetModuleHandle(_T("User32"));
1508 GetDpiForWindow_t pGetDpiForWindow =
1509 GetProcAddressAs(module,
"GetDpiForWindow", GetDpiForWindow_t);
1510 SetProcessDPIAware_t pSetProcessDPIAware =
1511 GetProcAddressAs(module,
"SetProcessDPIAware", SetProcessDPIAware_t);
1512 if (pGetDpiForWindow && pSetProcessDPIAware)
1514 const UINT dpiAwareness = pGetDpiForWindow(hWndParent);
1515 if (dpiAwareness != USER_DEFAULT_SCREEN_DPI)
1516 pSetProcessDPIAware();
1518 FreeLibrary(module);
1526 wfc->systemMenuInsertPosition = 6;
1528 wfc->hInstance = hInstance;
1529 wfc->cursor = LoadCursor(
nullptr, IDC_ARROW);
1530 wfc->icon = LoadIcon(GetModuleHandle(
nullptr), MAKEINTRESOURCE(IDI_ICON1));
1531 wfc->wndClassName = _tcsdup(_T(
"FreeRDP"));
1532 wfc->wndClass.cbSize =
sizeof(WNDCLASSEX);
1533 wfc->wndClass.style = CS_HREDRAW | CS_VREDRAW;
1534 wfc->wndClass.lpfnWndProc = wf_event_proc;
1535 wfc->wndClass.cbClsExtra = 0;
1536 wfc->wndClass.cbWndExtra = 0;
1537 wfc->wndClass.hCursor =
nullptr;
1538 wfc->wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
1539 wfc->wndClass.lpszMenuName =
nullptr;
1540 wfc->wndClass.lpszClassName = wfc->wndClassName;
1541 wfc->wndClass.hInstance = hInstance;
1542 wfc->wndClass.hIcon = wfc->icon;
1543 wfc->wndClass.hIconSm = wfc->icon;
1544 RegisterClassEx(&(wfc->wndClass));
1545 wfc->keyboardThread =
1546 CreateThread(
nullptr, 0, wf_keyboard_thread, (
void*)wfc, 0, &wfc->keyboardThreadId);
1548 if (!wfc->keyboardThread)
1551 wfc->common.thread =
1552 CreateThread(
nullptr, 0, wf_client_thread, (
void*)instance, 0, &wfc->mainThreadId);
1554 if (!wfc->common.thread)
1560static int wfreerdp_client_stop(rdpContext* context)
1563 wfContext* wfc = (wfContext*)context;
1566 PostThreadMessage(wfc->mainThreadId, WM_QUIT, 0, 0);
1567 rc = freerdp_client_common_stop(context);
1568 wfc->mainThreadId = 0;
1570 if (wfc->keyboardThread)
1572 PostThreadMessage(wfc->keyboardThreadId, WM_QUIT, 0, 0);
1573 (void)WaitForSingleObject(wfc->keyboardThread, INFINITE);
1574 (void)CloseHandle(wfc->keyboardThread);
1575 wfc->keyboardThread =
nullptr;
1576 wfc->keyboardThreadId = 0;
1582int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1584 pEntryPoints->Version = 1;
1585 pEntryPoints->Size =
sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1586 pEntryPoints->GlobalInit = wfreerdp_client_global_init;
1587 pEntryPoints->GlobalUninit = wfreerdp_client_global_uninit;
1588 pEntryPoints->ContextSize =
sizeof(wfContext);
1589 pEntryPoints->ClientNew = wfreerdp_client_new;
1590 pEntryPoints->ClientFree = wfreerdp_client_free;
1591 pEntryPoints->ClientStart = wfreerdp_client_start;
1592 pEntryPoints->ClientStop = wfreerdp_client_stop;
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT64 freerdp_settings_get_uint64(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id)
Returns a UINT64 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.