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)
307 const char* str_data = freerdp_get_logon_error_info_data(data);
308 const char* str_type = freerdp_get_logon_error_info_type(type);
310 if (!instance || !instance->context)
314 if (type == LOGON_MSG_SESSION_CONTINUE)
317 char* title =
nullptr;
319 winpr_asprintf(&title, &tlen,
"[%s] info",
322 char* message =
nullptr;
324 winpr_asprintf(&message, &mlen,
"Logon Error Info %s [%s]", str_data, str_type);
326 rc = sdl_show_dialog(instance->context, title, message, SHOW_DIALOG_ACCEPT_REJECT);
332static DWORD sdl_show_ceritifcate_dialog(rdpContext* context,
const char* title,
335 if (!sdl_push_user_event(SDL_EVENT_USER_CERT_DIALOG, title, message))
338 SDL_Event
event = {};
339 if (!sdl_wait_for_result(context, SDL_EVENT_USER_CERT_RESULT, &event))
341 return static_cast<DWORD
>(
event.user.code);
344static char* sdl_pem_cert(
const char* pem)
346 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
350 char* fp = freerdp_certificate_get_fingerprint(cert);
351 char* start = freerdp_certificate_get_validity(cert, TRUE);
352 char* end = freerdp_certificate_get_validity(cert, FALSE);
353 freerdp_certificate_free(cert);
357 winpr_asprintf(&str, &slen,
368DWORD sdl_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
369 const char* common_name,
const char* subject,
370 const char* issuer,
const char* new_fingerprint,
371 const char* old_subject,
const char* old_issuer,
372 const char* old_fingerprint, DWORD flags)
374 const char* type = type_str_for_flags(flags);
376 WINPR_ASSERT(instance);
377 WINPR_ASSERT(instance->context);
378 WINPR_ASSERT(instance->context->settings);
383 char* new_fp_str =
nullptr;
385 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
386 new_fp_str = sdl_pem_cert(new_fingerprint);
388 winpr_asprintf(&new_fp_str, &len,
"Thumbprint: %s\n", new_fingerprint);
393 char* old_fp_str =
nullptr;
395 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
396 old_fp_str = sdl_pem_cert(old_fingerprint);
398 winpr_asprintf(&old_fp_str, &olen,
"Thumbprint: %s\n", old_fingerprint);
400 const char* collission_str =
"";
401 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
404 "A matching entry with legacy SHA1 was found in local known_hosts2 store.\n"
405 "If you just upgraded from a FreeRDP version before 2.0 this is expected.\n"
406 "The hashing algorithm has been upgraded from SHA1 to SHA256.\n"
407 "All manually accepted certificates must be reconfirmed!\n"
411 char* title =
nullptr;
413 winpr_asprintf(&title, &tlen,
"Certificate for %s:%" PRIu16
" (%s) has changed", host, port,
416 char* message =
nullptr;
418 winpr_asprintf(&message, &mlen,
419 "New Certificate details:\n"
424 "Old Certificate details:\n"
429 "The above X.509 certificate does not match the certificate used for previous "
431 "This may indicate that the certificate has been tampered with.\n"
432 "Please contact the administrator of the RDP server and clarify.\n",
433 common_name, subject, issuer, new_fp_str, old_subject, old_issuer, old_fp_str,
436 const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
445DWORD sdl_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
446 const char* common_name,
const char* subject,
const char* issuer,
447 const char* fingerprint, DWORD flags)
449 const char* type = type_str_for_flags(flags);
454 char* fp_str =
nullptr;
456 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
457 fp_str = sdl_pem_cert(fingerprint);
459 winpr_asprintf(&fp_str, &len,
"Thumbprint: %s\n", fingerprint);
461 char* title =
nullptr;
463 winpr_asprintf(&title, &tlen,
"New certificate for %s:%" PRIu16
" (%s)", host, port, type);
465 char* message =
nullptr;
473 "The above X.509 certificate could not be verified, possibly because you do not have\n"
474 "the CA certificate in your certificate store, or the certificate has expired.\n"
475 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n",
476 common_name, subject, issuer, fp_str);
478 const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
485BOOL sdl_cert_dialog_show(
const char* title,
const char* message)
490 BUTTONID_CERT_ACCEPT_PERMANENT = 23,
491 BUTTONID_CERT_ACCEPT_TEMPORARY = 24,
492 BUTTONID_CERT_DENY = 25
494 const SDL_MessageBoxButtonData buttons[] = {
495 { 0, BUTTONID_CERT_ACCEPT_PERMANENT,
"permanent" },
496 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_CERT_ACCEPT_TEMPORARY,
"temporary" },
497 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_CERT_DENY,
"cancel" }
500 const SDL_MessageBoxData data = { SDL_MESSAGEBOX_WARNING,
nullptr, title, message,
501 ARRAYSIZE(buttons), buttons,
nullptr };
502 const int rc = SDL_ShowMessageBox(&data, &buttonid);
511 case BUTTONID_CERT_ACCEPT_PERMANENT:
514 case BUTTONID_CERT_ACCEPT_TEMPORARY:
523 return sdl_push_user_event(SDL_EVENT_USER_CERT_RESULT, value);
526BOOL sdl_message_dialog_show(
const char* title,
const char* message, Sint32 flags)
531 BUTTONID_SHOW_ACCEPT = 24,
532 BUTTONID_SHOW_DENY = 25
534 const SDL_MessageBoxButtonData buttons[] = {
535 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_SHOW_ACCEPT,
"accept" },
536 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_SHOW_DENY,
"cancel" }
539 const int button_cnt = (flags & SHOW_DIALOG_ACCEPT_REJECT) ? 2 : 1;
540 const SDL_MessageBoxData data = {
541 SDL_MESSAGEBOX_WARNING,
nullptr, title, message, button_cnt, buttons,
nullptr
543 const int rc = SDL_ShowMessageBox(&data, &buttonid);
552 case BUTTONID_SHOW_ACCEPT:
561 return sdl_push_user_event(SDL_EVENT_USER_SHOW_RESULT, value);
566 const std::vector<std::string> auth = {
"Username: ",
"Domain: ",
568 const std::vector<std::string> authPin = {
"Device: ",
"PIN: " };
569 const std::vector<std::string> fidoPin = {
"FIDO2 PIN: " };
570 const std::vector<std::string> gw = {
"GatewayUsername: ",
"GatewayDomain: ",
571 "GatewayPassword: " };
572 std::vector<std::string> prompt;
575 switch (args->result)
577 case AUTH_SMARTCARD_PIN:
598 std::vector<std::string> result;
600 auto parent = SDL_GetMouseFocus();
602 parent = SDL_GetKeyboardFocus();
606 std::vector<std::string> initial{ args->user ? args->user :
"Smartcard",
"" };
607 std::vector<Uint32> flags = { SdlInputWidgetPair::SDL_INPUT_READONLY,
608 SdlInputWidgetPair::SDL_INPUT_MASK };
609 if (args->result == AUTH_FIDO_PIN)
612 flags = { SdlInputWidgetPair::SDL_INPUT_MASK };
614 else if (args->result != AUTH_SMARTCARD_PIN)
616 initial = { args->user ? args->user :
"", args->domain ? args->domain :
"",
617 args->password ? args->password :
"" };
618 flags = { 0, 0, SdlInputWidgetPair::SDL_INPUT_MASK };
621 ssize_t selected = -1;
622 switch (args->result)
624 case AUTH_SMARTCARD_PIN:
638 ilist.parent(parent);
639 rc = ilist.run(result);
642 if ((result.size() < prompt.size()))
645 char* user =
nullptr;
646 char* domain =
nullptr;
650 if (args->result == AUTH_FIDO_PIN)
652 pwd = _strdup(result.at(0).c_str());
656 user = _strdup(result.at(0).c_str());
657 if (args->result == AUTH_SMARTCARD_PIN)
658 pwd = _strdup(result.at(1).c_str());
661 domain = _strdup(result.at(1).c_str());
662 pwd = _strdup(result.at(2).c_str());
667 return sdl_push_user_event(SDL_EVENT_USER_AUTH_RESULT, user, domain, pwd, rc);
670BOOL sdl_scard_dialog_show(
const char* title, Sint32 count,
const char** list)
672 std::vector<std::string> vlist;
673 vlist.reserve(WINPR_ASSERTING_INT_CAST(
size_t, count));
674 for (Sint32 x = 0; x < count; x++)
675 vlist.emplace_back(list[x]);
677 Sint32 value = slist.run();
678 return sdl_push_user_event(SDL_EVENT_USER_SCARD_RESULT, value);
681void sdl_dialogs_uninit()
686void 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.