24#include <freerdp/log.h>
25#include <freerdp/utils/passphrase.h>
26#include <freerdp/utils/smartcardlogon.h>
30#include "../sdl_context.hpp"
31#include "sdl_dialogs.hpp"
32#include "sdl_input_widget_pair.hpp"
33#include "sdl_input_widget_pair_list.hpp"
34#include "sdl_select.hpp"
35#include "sdl_select_list.hpp"
36#include "sdl_connection_dialog.hpp"
40 SHOW_DIALOG_ACCEPT_REJECT = 1,
41 SHOW_DIALOG_TIMED_ACCEPT = 2
44static const char* type_str_for_flags(UINT32 flags)
46 const char* type =
"RDP-Server";
48 if (flags & VERIFY_CERT_FLAG_GATEWAY)
51 if (flags & VERIFY_CERT_FLAG_REDIRECT)
52 type =
"RDP-Redirect";
56static BOOL sdl_wait_for_result(rdpContext* context, Uint32 type, SDL_Event* result)
58 const SDL_Event empty = {};
60 WINPR_ASSERT(context);
63 while (!freerdp_shall_disconnect_context(context))
66 const int rc = SDL_PeepEvents(result, 1, SDL_GETEVENT, type, type);
74static int sdl_show_dialog(rdpContext* context,
const char* title,
const char* message,
79 if (!sdl_push_user_event(SDL_EVENT_USER_SHOW_DIALOG, title, message, flags))
82 if (!sdl_wait_for_result(context, SDL_EVENT_USER_SHOW_RESULT, &event))
85 return event.user.code;
88BOOL sdl_authenticate_ex(freerdp* instance,
char** username,
char** password,
char** domain,
89 rdp_auth_reason reason)
103 case AUTH_SMARTCARD_PIN:
105 if ((*username) && (*password))
118 char* title =
nullptr;
119 size_t titlesize = 0;
120 winpr_asprintf(&title, &titlesize,
"Credentials required for %s", target);
122 CStringPtr guard(title, free);
135 auto sdl = get_context(instance);
136 if (!p && !
sdl->credentialsRead())
141 const BOOL fromStdin =
144 freerdp_passphrase_from_env(instance->context, title, line.data(), line.capacity());
145 if (!env && fromStdin)
146 env = freerdp_passphrase_read(instance->context, title, line.data(), line.capacity(),
154 sdl->setCredentialsRead();
160 if (!sdl_push_user_event(SDL_EVENT_USER_AUTH_DIALOG, title, u, d, p, reason))
163 if (!sdl_wait_for_result(instance->context, SDL_EVENT_USER_AUTH_RESULT, &event))
168 res = arg->result > 0;
173 *username = arg->user;
174 *domain = arg->domain;
175 *password = arg->password;
180BOOL sdl_choose_smartcard(freerdp* instance,
SmartcardCertInfo** cert_list, DWORD count,
181 DWORD* choice, BOOL gateway)
185 WINPR_ASSERT(instance);
186 WINPR_ASSERT(cert_list);
187 WINPR_ASSERT(choice);
189 std::vector<std::string> strlist;
190 std::vector<const char*> list;
191 for (DWORD i = 0; i < count; i++)
194 char* reader = ConvertWCharToUtf8Alloc(cert->reader,
nullptr);
195 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName,
nullptr);
200 winpr_asprintf(&msg, &len,
201 "%s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s",
202 container_name, reader, cert->userHint, cert->domainHint, cert->subject,
203 cert->issuer, cert->upn);
205 strlist.emplace_back(msg);
208 free(container_name);
210 auto& m = strlist.back();
211 list.push_back(m.c_str());
214 SDL_Event
event = {};
215 const char* title =
"Select a logon smartcard certificate";
217 title =
"Select a gateway logon smartcard certificate";
218 if (!sdl_push_user_event(SDL_EVENT_USER_SCARD_DIALOG, title, list.data(), count))
221 if (!sdl_wait_for_result(instance->context, SDL_EVENT_USER_SCARD_RESULT, &event))
224 res = (
event.user.code >= 0);
225 *choice =
static_cast<DWORD
>(
event.user.code);
230SSIZE_T sdl_retry_dialog(freerdp* instance,
const char* what,
size_t current,
231 [[maybe_unused]]
void* userarg)
233 WINPR_ASSERT(instance);
234 WINPR_ASSERT(instance->context);
237 auto sdl = get_context(instance->context);
238 auto settings = instance->context->settings;
242 sdl->getDialog().setTitle(
"Retry connection to %s",
245 if ((strcmp(what,
"arm-transport") != 0) && (strcmp(what,
"connection") != 0))
247 sdl->getDialog().showError(
"Unknown module %s, aborting", what);
253 if (strcmp(what,
"arm-transport") == 0)
254 sdl->getDialog().showWarn(
"[%s] Starting your VM. It may take up to 5 minutes", what);
259 sdl->getDialog().showError(
260 "Automatic reconnection disabled, terminating. Try to connect again later");
267 sdl->getDialog().showError(
268 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
269 "tech support for help if this keeps happening.",
274 sdl->getDialog().showInfo(
"[%s] retry %" PRIuz
"/%" PRIuz
", delaying %" PRIuz
275 "ms before next attempt",
276 what, current + 1, max, delay);
277 return WINPR_ASSERTING_INT_CAST(ssize_t, delay);
280BOOL sdl_present_gateway_message(freerdp* instance, [[maybe_unused]] UINT32 type,
281 BOOL isDisplayMandatory, BOOL isConsentMandatory,
size_t length,
282 const WCHAR* wmessage)
284 if (!isDisplayMandatory)
287 char* title =
nullptr;
289 winpr_asprintf(&title, &len,
"[gateway]");
292 if (isConsentMandatory)
293 flags = SHOW_DIALOG_ACCEPT_REJECT;
294 else if (isDisplayMandatory)
295 flags = SHOW_DIALOG_TIMED_ACCEPT;
296 char* message = ConvertWCharNToUtf8Alloc(wmessage, length,
nullptr);
298 const int rc = sdl_show_dialog(instance->context, title, message, flags);
304int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
306 if (!instance || !instance->context)
312 case LOGON_MSG_SESSION_CONTINUE:
318 const char* str_data = freerdp_get_logon_error_info_data(data);
319 const char* str_type = freerdp_get_logon_error_info_type(type);
321 auto sdl = get_context(instance);
322 sdl->getRailChannelContext().noteLogonPrompt();
323 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Logon Error Info %s [%s]", str_data, str_type);
327static DWORD sdl_show_ceritifcate_dialog(rdpContext* context,
const char* title,
330 if (!sdl_push_user_event(SDL_EVENT_USER_CERT_DIALOG, title, message))
333 SDL_Event
event = {};
334 if (!sdl_wait_for_result(context, SDL_EVENT_USER_CERT_RESULT, &event))
336 return static_cast<DWORD
>(
event.user.code);
339static char* sdl_pem_cert(
const char* pem)
341 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
345 char* fp = freerdp_certificate_get_fingerprint(cert);
346 char* start = freerdp_certificate_get_validity(cert, TRUE);
347 char* end = freerdp_certificate_get_validity(cert, FALSE);
348 freerdp_certificate_free(cert);
352 winpr_asprintf(&str, &slen,
363DWORD sdl_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
364 const char* common_name,
const char* subject,
365 const char* issuer,
const char* new_fingerprint,
366 const char* old_subject,
const char* old_issuer,
367 const char* old_fingerprint, DWORD flags)
369 const char* type = type_str_for_flags(flags);
371 WINPR_ASSERT(instance);
372 WINPR_ASSERT(instance->context);
373 WINPR_ASSERT(instance->context->settings);
378 char* new_fp_str =
nullptr;
380 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
381 new_fp_str = sdl_pem_cert(new_fingerprint);
383 winpr_asprintf(&new_fp_str, &len,
"Thumbprint: %s\n", new_fingerprint);
388 char* old_fp_str =
nullptr;
390 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
391 old_fp_str = sdl_pem_cert(old_fingerprint);
393 winpr_asprintf(&old_fp_str, &olen,
"Thumbprint: %s\n", old_fingerprint);
395 const char* collission_str =
"";
396 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
399 "A matching entry with legacy SHA1 was found in local known_hosts2 store.\n"
400 "If you just upgraded from a FreeRDP version before 2.0 this is expected.\n"
401 "The hashing algorithm has been upgraded from SHA1 to SHA256.\n"
402 "All manually accepted certificates must be reconfirmed!\n"
406 char* title =
nullptr;
408 winpr_asprintf(&title, &tlen,
"Certificate for %s:%" PRIu16
" (%s) has changed", host, port,
411 char* message =
nullptr;
413 winpr_asprintf(&message, &mlen,
414 "New Certificate details:\n"
419 "Old Certificate details:\n"
424 "The above X.509 certificate does not match the certificate used for previous "
426 "This may indicate that the certificate has been tampered with.\n"
427 "Please contact the administrator of the RDP server and clarify.\n",
428 common_name, subject, issuer, new_fp_str, old_subject, old_issuer, old_fp_str,
431 const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
440DWORD sdl_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
441 const char* common_name,
const char* subject,
const char* issuer,
442 const char* fingerprint, DWORD flags)
444 const char* type = type_str_for_flags(flags);
449 char* fp_str =
nullptr;
451 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
452 fp_str = sdl_pem_cert(fingerprint);
454 winpr_asprintf(&fp_str, &len,
"Thumbprint: %s\n", fingerprint);
456 char* title =
nullptr;
458 winpr_asprintf(&title, &tlen,
"New certificate for %s:%" PRIu16
" (%s)", host, port, type);
460 char* message =
nullptr;
468 "The above X.509 certificate could not be verified, possibly because you do not have\n"
469 "the CA certificate in your certificate store, or the certificate has expired.\n"
470 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n",
471 common_name, subject, issuer, fp_str);
473 const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
480BOOL sdl_cert_dialog_show(
const char* title,
const char* message)
485 BUTTONID_CERT_ACCEPT_PERMANENT = 23,
486 BUTTONID_CERT_ACCEPT_TEMPORARY = 24,
487 BUTTONID_CERT_DENY = 25
489 const SDL_MessageBoxButtonData buttons[] = {
490 { 0, BUTTONID_CERT_ACCEPT_PERMANENT,
"permanent" },
491 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_CERT_ACCEPT_TEMPORARY,
"temporary" },
492 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_CERT_DENY,
"cancel" }
495 const SDL_MessageBoxData data = { SDL_MESSAGEBOX_WARNING,
nullptr, title, message,
496 ARRAYSIZE(buttons), buttons,
nullptr };
497 const int rc = SDL_ShowMessageBox(&data, &buttonid);
506 case BUTTONID_CERT_ACCEPT_PERMANENT:
509 case BUTTONID_CERT_ACCEPT_TEMPORARY:
518 return sdl_push_user_event(SDL_EVENT_USER_CERT_RESULT, value);
521BOOL sdl_message_dialog_show(
const char* title,
const char* message, Sint32 flags)
526 BUTTONID_SHOW_ACCEPT = 24,
527 BUTTONID_SHOW_DENY = 25
529 const SDL_MessageBoxButtonData buttons[] = {
530 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_SHOW_ACCEPT,
"accept" },
531 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_SHOW_DENY,
"cancel" }
534 const int button_cnt = (flags & SHOW_DIALOG_ACCEPT_REJECT) ? 2 : 1;
535 const SDL_MessageBoxData data = {
536 SDL_MESSAGEBOX_WARNING,
nullptr, title, message, button_cnt, buttons,
nullptr
538 const int rc = SDL_ShowMessageBox(&data, &buttonid);
547 case BUTTONID_SHOW_ACCEPT:
556 return sdl_push_user_event(SDL_EVENT_USER_SHOW_RESULT, value);
561 const std::vector<std::string> auth = {
"Username: ",
"Domain: ",
563 const std::vector<std::string> authPin = {
"Device: ",
"PIN: " };
564 const std::vector<std::string> fidoPin = {
"FIDO2 PIN: " };
565 const std::vector<std::string> gw = {
"GatewayUsername: ",
"GatewayDomain: ",
566 "GatewayPassword: " };
567 std::vector<std::string> prompt;
570 switch (args->result)
572 case AUTH_SMARTCARD_PIN:
593 std::vector<std::string> result;
595 auto parent = SDL_GetMouseFocus();
597 parent = SDL_GetKeyboardFocus();
601 std::vector<std::string> initial{ args->user ? args->user :
"Smartcard",
"" };
602 std::vector<Uint32> flags = { SdlInputWidgetPair::SDL_INPUT_READONLY,
603 SdlInputWidgetPair::SDL_INPUT_MASK };
604 if (args->result == AUTH_FIDO_PIN)
607 flags = { SdlInputWidgetPair::SDL_INPUT_MASK };
609 else if (args->result != AUTH_SMARTCARD_PIN)
611 initial = { args->user ? args->user :
"", args->domain ? args->domain :
"",
612 args->password ? args->password :
"" };
613 flags = { 0, 0, SdlInputWidgetPair::SDL_INPUT_MASK };
616 ssize_t selected = -1;
617 switch (args->result)
619 case AUTH_SMARTCARD_PIN:
633 ilist.parent(parent);
634 rc = ilist.run(result);
637 if ((result.size() < prompt.size()))
640 char* user =
nullptr;
641 char* domain =
nullptr;
645 if (args->result == AUTH_FIDO_PIN)
647 pwd = _strdup(result.at(0).c_str());
651 user = _strdup(result.at(0).c_str());
652 if (args->result == AUTH_SMARTCARD_PIN)
653 pwd = _strdup(result.at(1).c_str());
656 domain = _strdup(result.at(1).c_str());
657 pwd = _strdup(result.at(2).c_str());
662 return sdl_push_user_event(SDL_EVENT_USER_AUTH_RESULT, user, domain, pwd, rc);
665BOOL sdl_scard_dialog_show(
const char* title, Sint32 count,
const char** list)
667 std::vector<std::string> vlist;
668 vlist.reserve(WINPR_ASSERTING_INT_CAST(
size_t, count));
669 for (Sint32 x = 0; x < count; x++)
670 vlist.emplace_back(list[x]);
672 Sint32 value = slist.run();
673 return sdl_push_user_event(SDL_EVENT_USER_SCARD_RESULT, value);
676void sdl_dialogs_uninit()
681void sdl_dialogs_init()
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_server_name(const rdpSettings *settings)
A helper function to return the correct server name.
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 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.