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