FreeRDP
Loading...
Searching...
No Matches
SDL2/dialogs/sdl_dialogs.cpp
1
20#include <vector>
21#include <string>
22#include <cassert>
23
24#include <freerdp/log.h>
25#include <freerdp/utils/smartcardlogon.h>
26
27#include <SDL.h>
28
29#include "../sdl_freerdp.hpp"
30#include "sdl_dialogs.hpp"
31#include "sdl_input.hpp"
32#include "sdl_input_widgets.hpp"
33#include "sdl_select.hpp"
34#include "sdl_selectlist.hpp"
35
36enum
37{
38 SHOW_DIALOG_ACCEPT_REJECT = 1,
39 SHOW_DIALOG_TIMED_ACCEPT = 2
40};
41
42static const char* type_str_for_flags(UINT32 flags)
43{
44 const char* type = "RDP-Server";
45
46 if (flags & VERIFY_CERT_FLAG_GATEWAY)
47 type = "RDP-Gateway";
48
49 if (flags & VERIFY_CERT_FLAG_REDIRECT)
50 type = "RDP-Redirect";
51 return type;
52}
53
54static BOOL sdl_wait_for_result(rdpContext* context, Uint32 type, SDL_Event* result)
55{
56 const SDL_Event empty = {};
57
58 WINPR_ASSERT(context);
59 WINPR_ASSERT(result);
60
61 while (!freerdp_shall_disconnect_context(context))
62 {
63 *result = empty;
64 const int rc = SDL_PeepEvents(result, 1, SDL_GETEVENT, type, type);
65 if (rc > 0)
66 return TRUE;
67 Sleep(1);
68 }
69 return FALSE;
70}
71
72static int sdl_show_dialog(rdpContext* context, const char* title, const char* message,
73 Sint32 flags)
74{
75 SDL_Event event = {};
76
77 if (!sdl_push_user_event(SDL_USEREVENT_SHOW_DIALOG, title, message, flags))
78 return 0;
79
80 if (!sdl_wait_for_result(context, SDL_USEREVENT_SHOW_RESULT, &event))
81 return 0;
82
83 return event.user.code;
84}
85
86BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
87 rdp_auth_reason reason)
88{
89 SDL_Event event = {};
90 BOOL res = FALSE;
91
92 SDLConnectionDialogHider hider(instance);
93
94 const char* target = freerdp_settings_get_server_name(instance->context->settings);
95 switch (reason)
96 {
97 case AUTH_NLA:
98 break;
99
100 case AUTH_TLS:
101 case AUTH_RDP:
102 case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
103 case AUTH_FIDO_PIN:
104 if ((*username) && (*password))
105 return TRUE;
106 break;
107 case GW_AUTH_HTTP:
108 case GW_AUTH_RDG:
109 case GW_AUTH_RPC:
110 target =
111 freerdp_settings_get_string(instance->context->settings, FreeRDP_GatewayHostname);
112 break;
113 default:
114 break;
115 }
116
117 char* title = nullptr;
118 size_t titlesize = 0;
119 winpr_asprintf(&title, &titlesize, "Credentials required for %s", target);
120
121 CStringPtr scope(title, free);
122 char* u = nullptr;
123 char* d = nullptr;
124 char* p = nullptr;
125
126 assert(username);
127 assert(domain);
128 assert(password);
129
130 u = *username;
131 d = *domain;
132 p = *password;
133
134 if (!sdl_push_user_event(SDL_USEREVENT_AUTH_DIALOG, title, u, d, p, reason))
135 return res;
136
137 if (!sdl_wait_for_result(instance->context, SDL_USEREVENT_AUTH_RESULT, &event))
138 return res;
139
140 auto arg = reinterpret_cast<SDL_UserAuthArg*>(event.padding);
141
142 res = arg->result > 0;
143
144 free(*username);
145 free(*domain);
146 free(*password);
147 *username = arg->user;
148 *domain = arg->domain;
149 *password = arg->password;
150
151 return res;
152}
153
154BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
155 DWORD* choice, BOOL gateway)
156{
157 BOOL res = FALSE;
158
159 WINPR_ASSERT(instance);
160 WINPR_ASSERT(cert_list);
161 WINPR_ASSERT(choice);
162
163 SDLConnectionDialogHider hider(instance);
164 std::vector<std::string> strlist;
165 std::vector<const char*> list;
166 for (DWORD i = 0; i < count; i++)
167 {
168 const SmartcardCertInfo* cert = cert_list[i];
169 char* reader = ConvertWCharToUtf8Alloc(cert->reader, nullptr);
170 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, nullptr);
171
172 char* msg = nullptr;
173 size_t len = 0;
174
175 winpr_asprintf(&msg, &len,
176 "%s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s",
177 container_name, reader, cert->userHint, cert->domainHint, cert->subject,
178 cert->issuer, cert->upn);
179
180 strlist.emplace_back(msg);
181 free(msg);
182 free(reader);
183 free(container_name);
184
185 auto& m = strlist.back();
186 list.push_back(m.c_str());
187 }
188
189 SDL_Event event = {};
190 const char* title = "Select a logon smartcard certificate";
191 if (gateway)
192 title = "Select a gateway logon smartcard certificate";
193 if (!sdl_push_user_event(SDL_USEREVENT_SCARD_DIALOG, title, list.data(), count))
194 return res;
195
196 if (!sdl_wait_for_result(instance->context, SDL_USEREVENT_SCARD_RESULT, &event))
197 return res;
198
199 res = (event.user.code >= 0);
200 *choice = static_cast<DWORD>(event.user.code);
201
202 return res;
203}
204
205SSIZE_T sdl_retry_dialog(freerdp* instance, const char* what, size_t current,
206 [[maybe_unused]] void* userarg)
207{
208 WINPR_ASSERT(instance);
209 WINPR_ASSERT(instance->context);
210 WINPR_ASSERT(what);
211
212 auto sdl = get_context(instance->context);
213 auto settings = instance->context->settings;
214 const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
215 std::scoped_lock lock(sdl->critical);
216 if (!sdl->connection_dialog)
217 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
218
219 sdl->connection_dialog->setTitle("Retry connection to %s",
220 freerdp_settings_get_server_name(instance->context->settings));
221
222 if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
223 {
224 sdl->connection_dialog->showError("Unknown module %s, aborting", what);
225 return -1;
226 }
227
228 if (current == 0)
229 {
230 if (strcmp(what, "arm-transport") == 0)
231 sdl->connection_dialog->showWarn("[%s] Starting your VM. It may take up to 5 minutes",
232 what);
233 }
234
235 const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
236
237 if (!enabled)
238 {
239 sdl->connection_dialog->showError(
240 "Automatic reconnection disabled, terminating. Try to connect again later");
241 return -1;
242 }
243
244 const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
245
246 if (current >= max)
247 {
248 sdl->connection_dialog->showError(
249 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
250 "tech support for help if this keeps happening.",
251 what);
252 return -1;
253 }
254
255 sdl->connection_dialog->showInfo("[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz
256 "ms before next attempt",
257 what, current + 1, max, delay);
258 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
259}
260
261BOOL sdl_present_gateway_message(freerdp* instance, [[maybe_unused]] UINT32 type,
262 BOOL isDisplayMandatory, BOOL isConsentMandatory, size_t length,
263 const WCHAR* wmessage)
264{
265 if (!isDisplayMandatory)
266 return TRUE;
267
268 char* title = nullptr;
269 size_t len = 0;
270 winpr_asprintf(&title, &len, "[gateway]");
271
272 Sint32 flags = 0;
273 if (isConsentMandatory)
274 flags = SHOW_DIALOG_ACCEPT_REJECT;
275 else if (isDisplayMandatory)
276 flags = SHOW_DIALOG_TIMED_ACCEPT;
277 char* message = ConvertWCharNToUtf8Alloc(wmessage, length, nullptr);
278
279 SDLConnectionDialogHider hider(instance);
280 const int rc = sdl_show_dialog(instance->context, title, message, flags);
281 free(title);
282 free(message);
283 return rc > 0;
284}
285
286int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
287{
288 int rc = -1;
289 const char* str_data = freerdp_get_logon_error_info_data(data);
290 const char* str_type = freerdp_get_logon_error_info_type(type);
291
292 if (!instance || !instance->context)
293 return -1;
294
295 /* ignore LOGON_MSG_SESSION_CONTINUE message */
296 if (type == LOGON_MSG_SESSION_CONTINUE)
297 return 0;
298
299 SDLConnectionDialogHider hider(instance);
300
301 char* title = nullptr;
302 size_t tlen = 0;
303 winpr_asprintf(&title, &tlen, "[%s] info",
304 freerdp_settings_get_server_name(instance->context->settings));
305
306 char* message = nullptr;
307 size_t mlen = 0;
308 winpr_asprintf(&message, &mlen, "Logon Error Info %s [%s]", str_data, str_type);
309
310 rc = sdl_show_dialog(instance->context, title, message, SHOW_DIALOG_ACCEPT_REJECT);
311 free(title);
312 free(message);
313 return rc;
314}
315
316static DWORD sdl_show_ceritifcate_dialog(rdpContext* context, const char* title,
317 const char* message)
318{
319 SDLConnectionDialogHider hider(context);
320 if (!sdl_push_user_event(SDL_USEREVENT_CERT_DIALOG, title, message))
321 return 0;
322
323 SDL_Event event = {};
324 if (!sdl_wait_for_result(context, SDL_USEREVENT_CERT_RESULT, &event))
325 return 0;
326 return static_cast<DWORD>(event.user.code);
327}
328
329static char* sdl_pem_cert(const char* pem)
330{
331 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
332 if (!cert)
333 return nullptr;
334
335 char* fp = freerdp_certificate_get_fingerprint(cert);
336 char* start = freerdp_certificate_get_validity(cert, TRUE);
337 char* end = freerdp_certificate_get_validity(cert, FALSE);
338 freerdp_certificate_free(cert);
339
340 char* str = nullptr;
341 size_t slen = 0;
342 winpr_asprintf(&str, &slen,
343 "Valid from: %s\n"
344 "Valid to: %s\n"
345 "Thumbprint: %s\n",
346 start, end, fp);
347 free(fp);
348 free(start);
349 free(end);
350 return str;
351}
352
353DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
354 const char* common_name, const char* subject,
355 const char* issuer, const char* new_fingerprint,
356 const char* old_subject, const char* old_issuer,
357 const char* old_fingerprint, DWORD flags)
358{
359 const char* type = type_str_for_flags(flags);
360
361 WINPR_ASSERT(instance);
362 WINPR_ASSERT(instance->context);
363 WINPR_ASSERT(instance->context->settings);
364
365 SDLConnectionDialogHider hider(instance);
366 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
367 * FreeRDP_CertificateCallbackPreferPEM to TRUE
368 */
369 char* new_fp_str = nullptr;
370 size_t len = 0;
371 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
372 new_fp_str = sdl_pem_cert(new_fingerprint);
373 else
374 winpr_asprintf(&new_fp_str, &len, "Thumbprint: %s\n", new_fingerprint);
375
376 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
377 * FreeRDP_CertificateCallbackPreferPEM to TRUE
378 */
379 char* old_fp_str = nullptr;
380 size_t olen = 0;
381 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
382 old_fp_str = sdl_pem_cert(old_fingerprint);
383 else
384 winpr_asprintf(&old_fp_str, &olen, "Thumbprint: %s\n", old_fingerprint);
385
386 const char* collission_str = "";
387 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
388 {
389 collission_str =
390 "A matching entry with legacy SHA1 was found in local known_hosts2 store.\n"
391 "If you just upgraded from a FreeRDP version before 2.0 this is expected.\n"
392 "The hashing algorithm has been upgraded from SHA1 to SHA256.\n"
393 "All manually accepted certificates must be reconfirmed!\n"
394 "\n";
395 }
396
397 char* title = nullptr;
398 size_t tlen = 0;
399 winpr_asprintf(&title, &tlen, "Certificate for %s:%" PRIu16 " (%s) has changed", host, port,
400 type);
401
402 char* message = nullptr;
403 size_t mlen = 0;
404 winpr_asprintf(&message, &mlen,
405 "New Certificate details:\n"
406 "Common Name: %s\n"
407 "Subject: %s\n"
408 "Issuer: %s\n"
409 "%s\n"
410 "Old Certificate details:\n"
411 "Subject: %s\n"
412 "Issuer: %s\n"
413 "%s\n"
414 "%s\n"
415 "The above X.509 certificate does not match the certificate used for previous "
416 "connections.\n"
417 "This may indicate that the certificate has been tampered with.\n"
418 "Please contact the administrator of the RDP server and clarify.\n",
419 common_name, subject, issuer, new_fp_str, old_subject, old_issuer, old_fp_str,
420 collission_str);
421
422 const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
423 free(title);
424 free(message);
425 free(new_fp_str);
426 free(old_fp_str);
427
428 return rc;
429}
430
431DWORD sdl_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
432 const char* common_name, const char* subject, const char* issuer,
433 const char* fingerprint, DWORD flags)
434{
435 const char* type = type_str_for_flags(flags);
436
437 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
438 * FreeRDP_CertificateCallbackPreferPEM to TRUE
439 */
440 char* fp_str = nullptr;
441 size_t len = 0;
442 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
443 fp_str = sdl_pem_cert(fingerprint);
444 else
445 winpr_asprintf(&fp_str, &len, "Thumbprint: %s\n", fingerprint);
446
447 char* title = nullptr;
448 size_t tlen = 0;
449 winpr_asprintf(&title, &tlen, "New certificate for %s:%" PRIu16 " (%s)", host, port, type);
450
451 char* message = nullptr;
452 size_t mlen = 0;
453 winpr_asprintf(
454 &message, &mlen,
455 "Common Name: %s\n"
456 "Subject: %s\n"
457 "Issuer: %s\n"
458 "%s\n"
459 "The above X.509 certificate could not be verified, possibly because you do not have\n"
460 "the CA certificate in your certificate store, or the certificate has expired.\n"
461 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n",
462 common_name, subject, issuer, fp_str);
463
464 SDLConnectionDialogHider hider(instance);
465 const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
466 free(fp_str);
467 free(title);
468 free(message);
469 return rc;
470}
471
472BOOL sdl_cert_dialog_show(const char* title, const char* message)
473{
474 int buttonid = -1;
475 enum
476 {
477 BUTTONID_CERT_ACCEPT_PERMANENT = 23,
478 BUTTONID_CERT_ACCEPT_TEMPORARY = 24,
479 BUTTONID_CERT_DENY = 25
480 };
481 const SDL_MessageBoxButtonData buttons[] = {
482 { 0, BUTTONID_CERT_ACCEPT_PERMANENT, "permanent" },
483 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_CERT_ACCEPT_TEMPORARY, "temporary" },
484 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_CERT_DENY, "cancel" }
485 };
486
487 const SDL_MessageBoxData data = { SDL_MESSAGEBOX_WARNING, nullptr, title, message,
488 ARRAYSIZE(buttons), buttons, nullptr };
489 const int rc = SDL_ShowMessageBox(&data, &buttonid);
490
491 Sint32 value = -1;
492 if (rc < 0)
493 value = 0;
494 else
495 {
496 switch (buttonid)
497 {
498 case BUTTONID_CERT_ACCEPT_PERMANENT:
499 value = 1;
500 break;
501 case BUTTONID_CERT_ACCEPT_TEMPORARY:
502 value = 2;
503 break;
504 default:
505 value = 0;
506 break;
507 }
508 }
509
510 return sdl_push_user_event(SDL_USEREVENT_CERT_RESULT, value);
511}
512
513BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags)
514{
515 int buttonid = -1;
516 enum
517 {
518 BUTTONID_SHOW_ACCEPT = 24,
519 BUTTONID_SHOW_DENY = 25
520 };
521 const SDL_MessageBoxButtonData buttons[] = {
522 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_SHOW_ACCEPT, "accept" },
523 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_SHOW_DENY, "cancel" }
524 };
525
526 const int button_cnt = (flags & SHOW_DIALOG_ACCEPT_REJECT) ? 2 : 1;
527 const SDL_MessageBoxData data = {
528 SDL_MESSAGEBOX_WARNING, nullptr, title, message, button_cnt, buttons, nullptr
529 };
530 const int rc = SDL_ShowMessageBox(&data, &buttonid);
531
532 Sint32 value = -1;
533 if (rc < 0)
534 value = 0;
535 else
536 {
537 switch (buttonid)
538 {
539 case BUTTONID_SHOW_ACCEPT:
540 value = 1;
541 break;
542 default:
543 value = 0;
544 break;
545 }
546 }
547
548 return sdl_push_user_event(SDL_USEREVENT_SHOW_RESULT, value);
549}
550
551BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args)
552{
553 const std::vector<std::string> auth = { "Username: ", "Domain: ",
554 "Password: " };
555 const std::vector<std::string> authPin = { "Device: ", "PIN: " };
556 const std::vector<std::string> fidoPin = { "FIDO2 PIN: " };
557 const std::vector<std::string> gw = { "GatewayUsername: ", "GatewayDomain: ",
558 "GatewayPassword: " };
559 std::vector<std::string> prompt;
560 Sint32 rc = -1;
561
562 switch (args->result)
563 {
564 case AUTH_SMARTCARD_PIN:
565 prompt = authPin;
566 break;
567 case AUTH_FIDO_PIN:
568 prompt = fidoPin;
569 break;
570 case AUTH_TLS:
571 case AUTH_RDP:
572 case AUTH_NLA:
573 prompt = auth;
574 break;
575 case GW_AUTH_HTTP:
576 case GW_AUTH_RDG:
577 case GW_AUTH_RPC:
578 prompt = gw;
579 break;
580 default:
581 break;
582 }
583
584 std::vector<std::string> result;
585
586 if (!prompt.empty())
587 {
588 std::vector<std::string> initial{ args->user ? args->user : "Smartcard", "" };
589 std::vector<Uint32> flags = { SdlInputWidget::SDL_INPUT_READONLY,
590 SdlInputWidget::SDL_INPUT_MASK };
591 if (args->result == AUTH_FIDO_PIN)
592 {
593 initial = { "" };
594 flags = { SdlInputWidget::SDL_INPUT_MASK };
595 }
596 else if (args->result != AUTH_SMARTCARD_PIN)
597 {
598 initial = { args->user ? args->user : "", args->domain ? args->domain : "",
599 args->password ? args->password : "" };
600 flags = { 0, 0, SdlInputWidget::SDL_INPUT_MASK };
601 }
602 SdlInputWidgetList ilist(args->title, prompt, initial, flags);
603 rc = ilist.run(result);
604 }
605
606 if ((result.size() < prompt.size()))
607 rc = -1;
608
609 char* user = nullptr;
610 char* domain = nullptr;
611 char* pwd = nullptr;
612 if (rc > 0)
613 {
614 if (args->result == AUTH_FIDO_PIN)
615 {
616 pwd = _strdup(result.at(0).c_str());
617 }
618 else
619 {
620 user = _strdup(result.at(0).c_str());
621 if (args->result == AUTH_SMARTCARD_PIN)
622 pwd = _strdup(result.at(1).c_str());
623 else
624 {
625 domain = _strdup(result.at(1).c_str());
626 pwd = _strdup(result.at(2).c_str());
627 }
628 }
629 }
630 return sdl_push_user_event(SDL_USEREVENT_AUTH_RESULT, user, domain, pwd, rc);
631}
632
633BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list)
634{
635 const auto scount = WINPR_ASSERTING_INT_CAST(size_t, count);
636 std::vector<std::string> vlist;
637 vlist.reserve(scount);
638 for (size_t x = 0; x < scount; x++)
639 vlist.emplace_back(list[x]);
640 SdlSelectList slist(title, vlist);
641 Sint32 value = slist.run();
642 return sdl_push_user_event(SDL_USEREVENT_SCARD_RESULT, value);
643}
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.