FreeRDP
Loading...
Searching...
No Matches
libfreerdp/crypto/tls.c
1
22#include <freerdp/config.h>
23
24#include "../core/settings.h"
25
26#include <winpr/assert.h>
27#include <string.h>
28#include <errno.h>
29
30#include <winpr/crt.h>
31#include <winpr/winpr.h>
32#include <winpr/string.h>
33#include <winpr/sspi.h>
34#include <winpr/ssl.h>
35#include <winpr/json.h>
36
37#include <winpr/stream.h>
38#include <freerdp/utils/ringbuffer.h>
39
40#include <freerdp/crypto/certificate.h>
41#include <freerdp/crypto/certificate_data.h>
42#include <freerdp/utils/helpers.h>
43
44#include <freerdp/log.h>
45#include "../crypto/tls.h"
46#include "../core/tcp.h"
47
48#include "opensslcompat.h"
49#include "certificate.h"
50#include "privatekey.h"
51#include "../core/utils.h"
52
53#ifdef WINPR_HAVE_POLL_H
54#include <poll.h>
55#endif
56
57#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
58#include <valgrind/memcheck.h>
59#endif
60
61#define TAG FREERDP_TAG("crypto")
62
85typedef struct
86{
87 SSL* ssl;
89#ifdef WITH_TLS_DATA_LIMIT
90 UINT64 rxBytes; /* plaintext bytes read since the last rekey */
91 UINT64 txBytes; /* plaintext bytes written since the last rekey */
92 BOOL rekeyAvailable; /* FALSE once we learn the connection is not TLS 1.3 */
93 BOOL cutPending; /* TRUE once a TLS 1.3 rekey failed; tear the connection down */
94#endif
95} BIO_RDP_TLS;
96
97#ifdef WITH_TLS_DATA_LIMIT
98/* Per-direction plaintext byte threshold before refreshing the TLS traffic keys,
99 * to stay within AEAD cryptographic limits. RFC 8446 (TLS 1.3) ยง5.5 bounds
100 * AES-GCM at 2^24.5 records (2^14 B) (~362 GiB). OpenSSL does not enforce this itself
101 * (https://github.com/openssl/openssl/issues/23566). On TLS 1.3 the keys are
102 * rekeyed in place (SSL_key_update); on other protocols a rekey is not possible. */
103static const UINT64 TLS_DATA_LIMIT_BYTES = 388736063996ULL;
104#endif
105
106static INIT_ONCE g_BioMethodsOnce = INIT_ONCE_STATIC_INIT;
107static BIO_METHOD* g_BioMethods = nullptr;
108
109static INIT_ONCE secrets_file_idx_once = INIT_ONCE_STATIC_INIT;
110static int secrets_file_idx = -1;
111
112WINPR_ATTR_NODISCARD
113static int tls_verify_certificate(rdpTls* tls, const rdpCertificate* cert, const char* hostname,
114 UINT16 port);
115
116static void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port,
117 const char* common_name, char** alt_names,
118 size_t alt_names_count);
119
120static void tls_print_new_certificate_warn(rdpCertificateStore* store, const char* hostname,
121 UINT16 port, const char* type, const char* fingerprint);
122
123static void tls_print_certificate_error(rdpCertificateStore* store, rdpCertificateData* stored_data,
124 const char* hostname, UINT16 port, const char* fingerprint);
125
126static void free_tls_public_key(rdpTls* tls)
127{
128 WINPR_ASSERT(tls);
129 free(tls->PublicKey);
130 tls->PublicKey = nullptr;
131 tls->PublicKeyLength = 0;
132}
133
134static void free_tls_bindings(SecPkgContext_Bindings* bindings)
135{
136 if (!bindings)
137 return;
138
139 free(bindings->Bindings);
140 free(bindings);
141}
142
143#ifdef WITH_TLS_DATA_LIMIT
144/* Must be called with tls->lock held, after a successful SSL_read/SSL_write of
145 * `count` bytes in the given direction. Once a per-direction byte threshold is
146 * crossed, refreshes the TLS 1.3 traffic keys in place; on a non-TLS-1.3
147 * connection a rekey is not possible so it is disabled (the connection is kept).
148 * If a TLS 1.3 rekey fails, flags the connection to be cut. */
149static void bio_rdp_tls_account(BIO_RDP_TLS* tls, BOOL read, int count)
150{
151 UINT64* counter = read ? &tls->rxBytes : &tls->txBytes;
152
153 if (!tls->rekeyAvailable || tls->cutPending)
154 return;
155
156 *counter += (UINT64)count;
157 if (*counter < TLS_DATA_LIMIT_BYTES)
158 return;
159
160 if (SSL_version(tls->ssl) != TLS1_3_VERSION)
161 {
162 WLog_DBG(TAG, "TLS data limit reached but connection is not TLS 1.3; "
163 "rekey unavailable, keeping connection");
164 tls->rekeyAvailable = FALSE;
165 return;
166 }
167
168 if (SSL_key_update(tls->ssl, SSL_KEY_UPDATE_REQUESTED) != 1)
169 {
170 WLog_ERR(TAG, "SSL_key_update failed at TLS data limit; cutting connection");
171 tls->cutPending = TRUE;
172 return;
173 }
174
175 WLog_INFO(TAG, "TLS 1.3 key update requested at data limit (%" PRIu64 " bytes %s)", *counter,
176 read ? "read" : "written");
177 *counter = 0;
178}
179
180static void bio_rdp_tls_read_account(BIO_RDP_TLS* tls, int count)
181{
182 bio_rdp_tls_account(tls, TRUE, count);
183}
184
185static void bio_rdp_tls_write_account(BIO_RDP_TLS* tls, int count)
186{
187 bio_rdp_tls_account(tls, FALSE, count);
188}
189#endif
190
191WINPR_ATTR_NODISCARD
192static int bio_rdp_tls_write(BIO* bio, const char* buf, int size)
193{
194 int error = 0;
195 int status = 0;
196 BIO_RDP_TLS* tls = (BIO_RDP_TLS*)BIO_get_data(bio);
197
198 if (!buf || !tls)
199 return 0;
200
201#ifdef WITH_TLS_DATA_LIMIT
202 if (tls->cutPending)
203 {
204 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
205 return -1;
206 }
207#endif
208
209 BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL);
210 EnterCriticalSection(&tls->lock);
211 status = SSL_write(tls->ssl, buf, size);
212 error = SSL_get_error(tls->ssl, status);
213#ifdef WITH_TLS_DATA_LIMIT
214 if (status > 0)
215 bio_rdp_tls_write_account(tls, status);
216#endif
217 LeaveCriticalSection(&tls->lock);
218
219 if (status <= 0)
220 {
221 switch (error)
222 {
223 case SSL_ERROR_NONE:
224 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
225 break;
226
227 case SSL_ERROR_WANT_WRITE:
228 BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
229 break;
230
231 case SSL_ERROR_WANT_READ:
232 BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
233 break;
234
235 case SSL_ERROR_WANT_X509_LOOKUP:
236 BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
237 BIO_set_retry_reason(bio, BIO_RR_SSL_X509_LOOKUP);
238 break;
239
240 case SSL_ERROR_WANT_CONNECT:
241 BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
242 BIO_set_retry_reason(bio, BIO_RR_CONNECT);
243 break;
244
245 case SSL_ERROR_SYSCALL:
246 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
247 break;
248
249 case SSL_ERROR_SSL:
250 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
251 break;
252 default:
253 break;
254 }
255 }
256
257 return status;
258}
259
260WINPR_ATTR_NODISCARD
261static int bio_rdp_tls_read(BIO* bio, char* buf, int size)
262{
263 int error = 0;
264 int status = 0;
265 BIO_RDP_TLS* tls = (BIO_RDP_TLS*)BIO_get_data(bio);
266
267 if (!buf || !tls)
268 return 0;
269
270#ifdef WITH_TLS_DATA_LIMIT
271 if (tls->cutPending)
272 {
273 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
274 return -1;
275 }
276#endif
277
278 BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL);
279 EnterCriticalSection(&tls->lock);
280 status = SSL_read(tls->ssl, buf, size);
281 error = SSL_get_error(tls->ssl, status);
282#ifdef WITH_TLS_DATA_LIMIT
283 if (status > 0)
284 bio_rdp_tls_read_account(tls, status);
285#endif
286 LeaveCriticalSection(&tls->lock);
287
288 if (status <= 0)
289 {
290
291 switch (error)
292 {
293 case SSL_ERROR_NONE:
294 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
295 break;
296
297 case SSL_ERROR_WANT_READ:
298 BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
299 break;
300
301 case SSL_ERROR_WANT_WRITE:
302 BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
303 break;
304
305 case SSL_ERROR_WANT_X509_LOOKUP:
306 BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
307 BIO_set_retry_reason(bio, BIO_RR_SSL_X509_LOOKUP);
308 break;
309
310 case SSL_ERROR_WANT_ACCEPT:
311 BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
312 BIO_set_retry_reason(bio, BIO_RR_ACCEPT);
313 break;
314
315 case SSL_ERROR_WANT_CONNECT:
316 BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
317 BIO_set_retry_reason(bio, BIO_RR_CONNECT);
318 break;
319
320 case SSL_ERROR_SSL:
321 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
322 break;
323
324 case SSL_ERROR_ZERO_RETURN:
325 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
326 break;
327
328 case SSL_ERROR_SYSCALL:
329 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
330 break;
331 default:
332 break;
333 }
334 }
335
336#ifdef FREERDP_HAVE_VALGRIND_MEMCHECK_H
337
338 if (status > 0)
339 {
340 VALGRIND_MAKE_MEM_DEFINED(buf, status);
341 }
342
343#endif
344 return status;
345}
346
347WINPR_ATTR_NODISCARD
348static int bio_rdp_tls_puts(BIO* bio, const char* str)
349{
350 if (!str)
351 return 0;
352
353 const size_t max = (INT_MAX > SIZE_MAX) ? SIZE_MAX : INT_MAX;
354 const size_t size = strnlen(str, max);
355 if (size >= max)
356 return -1;
357 ERR_clear_error();
358 return BIO_write(bio, str, (int)size);
359}
360
361WINPR_ATTR_NODISCARD
362static int bio_rdp_tls_gets(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED char* str,
363 WINPR_ATTR_UNUSED int size)
364{
365 return 1;
366}
367
368WINPR_ATTR_NODISCARD
369static long bio_rdp_tls_ctrl(BIO* bio, int cmd, long num, void* ptr)
370{
371 BIO* ssl_rbio = nullptr;
372 BIO* ssl_wbio = nullptr;
373 BIO* next_bio = nullptr;
374 long status = -1;
375 BIO_RDP_TLS* tls = (BIO_RDP_TLS*)BIO_get_data(bio);
376
377 if (!tls)
378 return 0;
379
380 if (!tls->ssl && (cmd != BIO_C_SET_SSL))
381 return 0;
382
383 next_bio = BIO_next(bio);
384 ssl_rbio = tls->ssl ? SSL_get_rbio(tls->ssl) : nullptr;
385 ssl_wbio = tls->ssl ? SSL_get_wbio(tls->ssl) : nullptr;
386
387 switch (cmd)
388 {
389 case BIO_CTRL_RESET:
390 SSL_shutdown(tls->ssl);
391
392 if (SSL_in_connect_init(tls->ssl))
393 SSL_set_connect_state(tls->ssl);
394 else if (SSL_in_accept_init(tls->ssl))
395 SSL_set_accept_state(tls->ssl);
396
397 SSL_clear(tls->ssl);
398
399 if (next_bio)
400 status = BIO_ctrl(next_bio, cmd, num, ptr);
401 else if (ssl_rbio)
402 status = BIO_ctrl(ssl_rbio, cmd, num, ptr);
403 else
404 status = 1;
405
406 break;
407
408 case BIO_C_GET_FD:
409 status = BIO_ctrl(ssl_rbio, cmd, num, ptr);
410 break;
411
412 case BIO_CTRL_INFO:
413 status = 0;
414 break;
415
416 case BIO_CTRL_SET_CALLBACK:
417 status = 0;
418 break;
419
420 case BIO_CTRL_GET_CALLBACK:
421 /* The OpenSSL API is horrible here:
422 * we get a function pointer returned and have to cast it to ULONG_PTR
423 * to return the value to the caller.
424 *
425 * This, of course, is something compilers warn about. So silence it by casting */
426 {
427 void* vptr = WINPR_FUNC_PTR_CAST(SSL_get_info_callback(tls->ssl), void*);
428 *((void**)ptr) = vptr;
429 status = 1;
430 }
431 break;
432
433 case BIO_C_SSL_MODE:
434 if (num)
435 SSL_set_connect_state(tls->ssl);
436 else
437 SSL_set_accept_state(tls->ssl);
438
439 status = 1;
440 break;
441
442 case BIO_CTRL_GET_CLOSE:
443 status = BIO_get_shutdown(bio);
444 break;
445
446 case BIO_CTRL_SET_CLOSE:
447 BIO_set_shutdown(bio, (int)num);
448 status = 1;
449 break;
450
451 case BIO_CTRL_WPENDING:
452 status = BIO_ctrl(ssl_wbio, cmd, num, ptr);
453 break;
454
455 case BIO_CTRL_PENDING:
456 status = SSL_pending(tls->ssl);
457
458 if (status == 0)
459 status = BIO_pending(ssl_rbio);
460
461 break;
462
463 case BIO_CTRL_FLUSH:
464 BIO_clear_retry_flags(bio);
465 status = BIO_ctrl(ssl_wbio, cmd, num, ptr);
466 if (status != 1)
467 WLog_DBG(TAG, "BIO_ctrl returned %ld", status);
468 BIO_copy_next_retry(bio);
469 status = 1;
470 break;
471
472 case BIO_CTRL_PUSH:
473 if (next_bio && (next_bio != ssl_rbio))
474 {
475#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
476 (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
477 SSL_set_bio(tls->ssl, next_bio, next_bio);
478 CRYPTO_add(&(bio->next_bio->references), 1, CRYPTO_LOCK_BIO);
479#else
480 /*
481 * We are going to pass ownership of next to the SSL object...but
482 * we don't own a reference to pass yet - so up ref
483 */
484 BIO_up_ref(next_bio);
485 SSL_set_bio(tls->ssl, next_bio, next_bio);
486#endif
487 }
488
489 status = 1;
490 break;
491
492 case BIO_CTRL_POP:
493
494 /* Only detach if we are the BIO explicitly being popped */
495 if (bio == ptr)
496 {
497 if (ssl_rbio != ssl_wbio)
498 BIO_free_all(ssl_wbio);
499
500#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
501 (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
502
503 if (next_bio)
504 CRYPTO_add(&(bio->next_bio->references), -1, CRYPTO_LOCK_BIO);
505
506 tls->ssl->wbio = tls->ssl->rbio = nullptr;
507#else
508 /* OpenSSL 1.1: This will also clear the reference we obtained during push */
509 SSL_set_bio(tls->ssl, nullptr, nullptr);
510#endif
511 }
512
513 status = 1;
514 break;
515
516 case BIO_C_GET_SSL:
517 if (ptr)
518 {
519 *((SSL**)ptr) = tls->ssl;
520 status = 1;
521 }
522
523 break;
524
525 case BIO_C_SET_SSL:
526 BIO_set_shutdown(bio, (int)num);
527
528 if (ptr)
529 {
530 tls->ssl = (SSL*)ptr;
531 ssl_rbio = SSL_get_rbio(tls->ssl);
532 }
533
534 if (ssl_rbio)
535 {
536 if (next_bio)
537 BIO_push(ssl_rbio, next_bio);
538
539 BIO_set_next(bio, ssl_rbio);
540#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
541 (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
542 CRYPTO_add(&(ssl_rbio->references), 1, CRYPTO_LOCK_BIO);
543#else
544 BIO_up_ref(ssl_rbio);
545#endif
546 }
547
548 BIO_set_init(bio, 1);
549 status = 1;
550 break;
551
552 case BIO_C_DO_STATE_MACHINE:
553 BIO_clear_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_WRITE | BIO_FLAGS_IO_SPECIAL);
554 BIO_set_retry_reason(bio, 0);
555 status = SSL_do_handshake(tls->ssl);
556
557 if (status <= 0)
558 {
559 const int err = (status < INT32_MIN) ? INT32_MIN : (int)status;
560 switch (SSL_get_error(tls->ssl, err))
561 {
562 case SSL_ERROR_WANT_READ:
563 BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
564 break;
565
566 case SSL_ERROR_WANT_WRITE:
567 BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
568 break;
569
570 case SSL_ERROR_WANT_CONNECT:
571 BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY);
572 BIO_set_retry_reason(bio, BIO_get_retry_reason(next_bio));
573 break;
574
575 default:
576 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
577 break;
578 }
579 }
580
581 break;
582
583 default:
584 status = BIO_ctrl(ssl_rbio, cmd, num, ptr);
585 break;
586 }
587
588 return status;
589}
590
591WINPR_ATTR_NODISCARD
592static int bio_rdp_tls_new(BIO* bio)
593{
594 BIO_RDP_TLS* tls = nullptr;
595 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
596
597 if (!(tls = calloc(1, sizeof(BIO_RDP_TLS))))
598 return 0;
599
600 if (!InitializeCriticalSectionAndSpinCount(&tls->lock, 4000))
601 {
602 free(tls);
603 return -1;
604 }
605
606#ifdef WITH_TLS_DATA_LIMIT
607 /* counters/cutPending zeroed by calloc; rekey is assumed possible until we
608 * observe a non-TLS-1.3 connection at the data limit. */
609 tls->rekeyAvailable = TRUE;
610#endif
611
612 BIO_set_data(bio, (void*)tls);
613 return 1;
614}
615
616WINPR_ATTR_NODISCARD
617static int bio_rdp_tls_free(BIO* bio)
618{
619 BIO_RDP_TLS* tls = nullptr;
620
621 if (!bio)
622 return 0;
623
624 tls = (BIO_RDP_TLS*)BIO_get_data(bio);
625
626 if (!tls)
627 return 0;
628
629 BIO_set_data(bio, nullptr);
630 if (BIO_get_shutdown(bio))
631 {
632 if (BIO_get_init(bio) && tls->ssl)
633 {
634 SSL_shutdown(tls->ssl);
635 SSL_free(tls->ssl);
636 }
637
638 BIO_set_init(bio, 0);
639 BIO_set_flags(bio, 0);
640 }
641
642 DeleteCriticalSection(&tls->lock);
643 free(tls);
644
645 return 1;
646}
647
648WINPR_ATTR_NODISCARD
649static long bio_rdp_tls_callback_ctrl(BIO* bio, int cmd, bio_info_cb* fp)
650{
651 long status = 0;
652
653 if (!bio)
654 return 0;
655
656 BIO_RDP_TLS* tls = (BIO_RDP_TLS*)BIO_get_data(bio);
657
658 if (!tls)
659 return 0;
660
661 switch (cmd)
662 {
663 case BIO_CTRL_SET_CALLBACK:
664 {
665 typedef void (*fkt_t)(const SSL*, int, int);
666
667 /* Documented since https://www.openssl.org/docs/man1.1.1/man3/BIO_set_callback.html
668 * the argument is not really of type bio_info_cb* and must be cast
669 * to the required type */
670
671 fkt_t fkt = WINPR_FUNC_PTR_CAST(fp, fkt_t);
672 SSL_set_info_callback(tls->ssl, fkt);
673 status = 1;
674 }
675 break;
676
677 default:
678 status = BIO_callback_ctrl(SSL_get_rbio(tls->ssl), cmd, fp);
679 break;
680 }
681
682 return status;
683}
684
685#define BIO_TYPE_RDP_TLS 68
686
687WINPR_ATTR_NODISCARD
688static BOOL CALLBACK bio_rdp_tls_init_once(WINPR_ATTR_UNUSED PINIT_ONCE once,
689 WINPR_ATTR_UNUSED PVOID param,
690 WINPR_ATTR_UNUSED PVOID* context)
691{
692 g_BioMethods = BIO_meth_new(BIO_TYPE_RDP_TLS, "RdpTls");
693 if (!g_BioMethods)
694 return FALSE;
695
696 if (BIO_meth_set_write(g_BioMethods, bio_rdp_tls_write) != 1)
697 return FALSE;
698 if (BIO_meth_set_read(g_BioMethods, bio_rdp_tls_read) != 1)
699 return FALSE;
700 if (BIO_meth_set_puts(g_BioMethods, bio_rdp_tls_puts) != 1)
701 return FALSE;
702 if (BIO_meth_set_gets(g_BioMethods, bio_rdp_tls_gets) != 1)
703 return FALSE;
704 if (BIO_meth_set_ctrl(g_BioMethods, bio_rdp_tls_ctrl) != 1)
705 return FALSE;
706 if (BIO_meth_set_create(g_BioMethods, bio_rdp_tls_new) != 1)
707 return FALSE;
708 if (BIO_meth_set_destroy(g_BioMethods, bio_rdp_tls_free) != 1)
709 return FALSE;
710 if (BIO_meth_set_callback_ctrl(g_BioMethods, bio_rdp_tls_callback_ctrl) != 1)
711 return FALSE;
712
713 return TRUE;
714}
715
716WINPR_ATTR_NODISCARD
717static BIO_METHOD* BIO_s_rdp_tls(void)
718{
719 if (!InitOnceExecuteOnce(&g_BioMethodsOnce, bio_rdp_tls_init_once, nullptr, nullptr))
720 return nullptr;
721 return g_BioMethods;
722}
723
724WINPR_ATTR_MALLOC(BIO_free, 1)
725static BIO* BIO_new_rdp_tls(SSL_CTX* ctx, int client)
726{
727 BIO* bio = BIO_new(BIO_s_rdp_tls());
728
729 if (!bio)
730 return nullptr;
731
732 SSL* ssl = SSL_new(ctx);
733
734 if (!ssl)
735 {
736 BIO_free_all(bio);
737 return nullptr;
738 }
739
740 if (client)
741 SSL_set_connect_state(ssl);
742 else
743 SSL_set_accept_state(ssl);
744
745 BIO_set_ssl(bio, ssl, BIO_CLOSE);
746 return bio;
747}
748
749WINPR_ATTR_MALLOC(freerdp_certificate_free, 1)
750static rdpCertificate* tls_get_certificate(rdpTls* tls, BOOL peer)
751{
752 X509* remote_cert = nullptr;
753
754 if (peer)
755 remote_cert = SSL_get_peer_certificate(tls->ssl);
756 else
757 remote_cert = X509_dup(SSL_get_certificate(tls->ssl));
758
759 if (!remote_cert)
760 {
761 WLog_ERR(TAG, "failed to get the server TLS certificate");
762 return nullptr;
763 }
764
765 /* Get the peer's chain. If it does not exist, we're setting nullptr (clean data either way) */
766 STACK_OF(X509)* chain = SSL_get_peer_cert_chain(tls->ssl);
767 rdpCertificate* cert = freerdp_certificate_new_from_x509(remote_cert, chain);
768 X509_free(remote_cert);
769
770 return cert;
771}
772
773WINPR_ATTR_NODISCARD
774static const char* tls_get_server_name(rdpTls* tls)
775{
776 return tls->serverName ? tls->serverName : tls->hostname;
777}
778
779#define TLS_SERVER_END_POINT "tls-server-end-point:"
780
781WINPR_ATTR_MALLOC(free_tls_bindings, 1)
782static SecPkgContext_Bindings* tls_get_channel_bindings(const rdpCertificate* cert)
783{
784 size_t CertificateHashLength = 0;
785 BYTE* ChannelBindingToken = nullptr;
786 SEC_CHANNEL_BINDINGS* ChannelBindings = nullptr;
787 const size_t PrefixLength = strnlen(TLS_SERVER_END_POINT, ARRAYSIZE(TLS_SERVER_END_POINT));
788
789 WINPR_ASSERT(cert);
790
791 /* See https://www.rfc-editor.org/rfc/rfc5929 for details about hashes */
792 WINPR_MD_TYPE alg = freerdp_certificate_get_signature_alg(cert);
793 const char* hash = nullptr;
794 switch (alg)
795 {
796
797 case WINPR_MD_MD5:
798 case WINPR_MD_SHA1:
799 hash = winpr_md_type_to_string(WINPR_MD_SHA256);
800 break;
801 default:
802 hash = winpr_md_type_to_string(alg);
803 break;
804 }
805 if (!hash)
806 return nullptr;
807
808 char* CertificateHash = freerdp_certificate_get_hash(cert, hash, &CertificateHashLength);
809 if (!CertificateHash)
810 return nullptr;
811
812 const size_t ChannelBindingTokenLength = PrefixLength + CertificateHashLength;
813 SecPkgContext_Bindings* ContextBindings = calloc(1, sizeof(SecPkgContext_Bindings));
814
815 if (!ContextBindings)
816 goto out_free;
817
818 {
819 const size_t slen = sizeof(SEC_CHANNEL_BINDINGS) + ChannelBindingTokenLength;
820 if (slen > UINT32_MAX)
821 goto out_free;
822
823 ContextBindings->BindingsLength = (UINT32)slen;
824 }
825 ChannelBindings = (SEC_CHANNEL_BINDINGS*)calloc(1, ContextBindings->BindingsLength);
826
827 if (!ChannelBindings)
828 goto out_free;
829
830 ContextBindings->Bindings = ChannelBindings;
831 ChannelBindings->cbApplicationDataLength = (UINT32)ChannelBindingTokenLength;
832 ChannelBindings->dwApplicationDataOffset = sizeof(SEC_CHANNEL_BINDINGS);
833 ChannelBindingToken = &((BYTE*)ChannelBindings)[ChannelBindings->dwApplicationDataOffset];
834 memcpy(ChannelBindingToken, TLS_SERVER_END_POINT, PrefixLength);
835 memcpy(ChannelBindingToken + PrefixLength, CertificateHash, CertificateHashLength);
836 free(CertificateHash);
837 return ContextBindings;
838out_free:
839 free(CertificateHash);
840 free(ContextBindings);
841 return nullptr;
842}
843
844WINPR_ATTR_NODISCARD
845static BOOL CALLBACK secrets_file_init_cb(WINPR_ATTR_UNUSED PINIT_ONCE once,
846 WINPR_ATTR_UNUSED PVOID param,
847 WINPR_ATTR_UNUSED PVOID* context)
848{
849 secrets_file_idx = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
850
851 return (secrets_file_idx != -1);
852}
853
854static void SSLCTX_keylog_cb(const SSL* ssl, const char* line)
855{
856 char* dfile = nullptr;
857
858 if (secrets_file_idx == -1)
859 return;
860
861 dfile = SSL_get_ex_data(ssl, secrets_file_idx);
862 if (dfile)
863 {
864 FILE* f = winpr_fopen(dfile, "a+");
865 if (f)
866 {
867 (void)fwrite(line, strlen(line), 1, f);
868 (void)fwrite("\n", 1, 1, f);
869 (void)fclose(f);
870 }
871 }
872}
873
874static void tls_reset(rdpTls* tls)
875{
876 WINPR_ASSERT(tls);
877
878 if (tls->ctx)
879 {
880 SSL_CTX_free(tls->ctx);
881 tls->ctx = nullptr;
882 }
883
884 /* tls->underlying is a stacked BIO under tls->bio.
885 * BIO_free_all will free recursively. */
886 if (tls->bio)
887 BIO_free_all(tls->bio);
888 else if (tls->underlying)
889 BIO_free_all(tls->underlying);
890 tls->bio = nullptr;
891 tls->underlying = nullptr;
892
893 free_tls_public_key(tls);
894 free_tls_bindings(tls->Bindings);
895 tls->Bindings = nullptr;
896}
897
898#if OPENSSL_VERSION_NUMBER >= 0x010000000L
899WINPR_ATTR_NODISCARD
900static BOOL tls_prepare(rdpTls* tls, BIO* underlying, const SSL_METHOD* method, int options,
901 BOOL clientMode)
902#else
903WINPR_ATTR_NODISCARD
904static BOOL tls_prepare(rdpTls* tls, BIO* underlying, SSL_METHOD* method, int options,
905 BOOL clientMode)
906#endif
907{
908 WINPR_ASSERT(tls);
909
910 rdpSettings* settings = tls->context->settings;
911 WINPR_ASSERT(settings);
912
913 tls_reset(tls);
914 tls->ctx = SSL_CTX_new(method);
915
916 tls->underlying = underlying;
917
918 if (!tls->ctx)
919 {
920 WLog_ERR(TAG, "SSL_CTX_new failed");
921 return FALSE;
922 }
923
924 SSL_CTX_set_mode(tls->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);
925 SSL_CTX_set_options(tls->ctx, WINPR_ASSERTING_INT_CAST(uint64_t, options));
926 SSL_CTX_set_read_ahead(tls->ctx, 1);
927#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
928 UINT16 version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMinVersion);
929 if (!SSL_CTX_set_min_proto_version(tls->ctx, version))
930 {
931 WLog_ERR(TAG, "SSL_CTX_set_min_proto_version %" PRIu16 " failed", version);
932 return FALSE;
933 }
934 version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMaxVersion);
935 if (!SSL_CTX_set_max_proto_version(tls->ctx, version))
936 {
937 WLog_ERR(TAG, "SSL_CTX_set_max_proto_version %" PRIu16 " failed", version);
938 return FALSE;
939 }
940#endif
941#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
942 SSL_CTX_set_security_level(tls->ctx, WINPR_ASSERTING_INT_CAST(int, settings->TlsSecLevel));
943#endif
944
945 if (settings->AllowedTlsCiphers)
946 {
947 if (!SSL_CTX_set_cipher_list(tls->ctx, settings->AllowedTlsCiphers))
948 {
949 WLog_ERR(TAG, "SSL_CTX_set_cipher_list %s failed", settings->AllowedTlsCiphers);
950 return FALSE;
951 }
952 }
953
954 tls->bio = BIO_new_rdp_tls(tls->ctx, clientMode);
955
956 if (!tls->bio)
957 {
958 WLog_ERR(TAG, "unable to create TLS BIO");
959 return FALSE;
960 }
961
962 if (BIO_get_ssl(tls->bio, &tls->ssl) < 0 || !tls->ssl)
963 {
964 WLog_ERR(TAG, "unable to retrieve the SSL of the connection");
965 return FALSE;
966 }
967
968 if (settings->TlsSecretsFile)
969 {
970#if OPENSSL_VERSION_NUMBER >= 0x10101000L
971 if (!InitOnceExecuteOnce(&secrets_file_idx_once, secrets_file_init_cb, nullptr, nullptr))
972 return FALSE;
973
974 if (secrets_file_idx != -1)
975 {
976 SSL_set_ex_data(tls->ssl, secrets_file_idx, settings->TlsSecretsFile);
977 SSL_CTX_set_keylog_callback(tls->ctx, SSLCTX_keylog_cb);
978 }
979#else
980 WLog_WARN(TAG, "Key-Logging not available - requires OpenSSL 1.1.1 or higher");
981#endif
982 }
983
984 BIO_push(tls->bio, underlying);
985 return TRUE;
986}
987
988static void
989adjustSslOptions(WINPR_ATTR_UNUSED int* options) // NOLINT(readability-non-const-parameter)
990{
991 WINPR_ASSERT(options);
992#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
993 *options |= SSL_OP_NO_SSLv2;
994 *options |= SSL_OP_NO_SSLv3;
995#endif
996}
997
998const SSL_METHOD* freerdp_tls_get_ssl_method(BOOL isDtls, BOOL isClient)
999{
1000 if (isClient)
1001 {
1002 if (isDtls)
1003 return DTLS_client_method();
1004#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
1005 return SSLv23_client_method();
1006#else
1007 return TLS_client_method();
1008#endif
1009 }
1010
1011 if (isDtls)
1012 return DTLS_server_method();
1013
1014#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
1015 return SSLv23_server_method();
1016#else
1017 return TLS_server_method();
1018#endif
1019}
1020
1021TlsHandshakeResult freerdp_tls_connect_ex(rdpTls* tls, BIO* underlying, const SSL_METHOD* methods)
1022{
1023 WINPR_ASSERT(tls);
1024
1025 int options = 0;
1035#ifdef SSL_OP_NO_COMPRESSION
1036 options |= SSL_OP_NO_COMPRESSION;
1037#endif
1044 options |= SSL_OP_TLS_BLOCK_PADDING_BUG;
1051 options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
1052
1053 tls->isClientMode = TRUE;
1054 adjustSslOptions(&options);
1055
1056 if (!tls_prepare(tls, underlying, methods, options, TRUE))
1057 return TLS_HANDSHAKE_ERROR;
1058
1059#if !defined(OPENSSL_NO_TLSEXT)
1060 const char* str = tls_get_server_name(tls);
1061 void* ptr = WINPR_CAST_CONST_PTR_AWAY(str, void*);
1062 SSL_set_tlsext_host_name(tls->ssl, ptr);
1063#endif
1064
1065 return freerdp_tls_handshake(tls);
1066}
1067
1068static int bio_err_print(const char* str, size_t len, void* u)
1069{
1070 wLog* log = u;
1071 WLog_Print(log, WLOG_ERROR, "[BIO_do_handshake] %s [%" PRIuz "]", str, len);
1072 return 0;
1073}
1074
1075TlsHandshakeResult freerdp_tls_handshake(rdpTls* tls)
1076{
1077 TlsHandshakeResult ret = TLS_HANDSHAKE_ERROR;
1078
1079 WINPR_ASSERT(tls);
1080 const long status = BIO_do_handshake(tls->bio);
1081 if (status != 1)
1082 {
1083 if (!BIO_should_retry(tls->bio))
1084 {
1085 wLog* log = WLog_Get(TAG);
1086 WLog_Print(log, WLOG_ERROR, "BIO_do_handshake failed");
1087 ERR_print_errors_cb(bio_err_print, log);
1088 return TLS_HANDSHAKE_ERROR;
1089 }
1090
1091 return TLS_HANDSHAKE_CONTINUE;
1092 }
1093
1094 int verify_status = 0;
1095 rdpCertificate* cert = tls_get_certificate(tls, tls->isClientMode);
1096
1097 if (!cert)
1098 {
1099 WLog_ERR(TAG, "tls_get_certificate failed to return the server certificate.");
1100 return TLS_HANDSHAKE_ERROR;
1101 }
1102
1103 do
1104 {
1105 free_tls_bindings(tls->Bindings);
1106 tls->Bindings = tls_get_channel_bindings(cert);
1107 if (!tls->Bindings)
1108 {
1109 WLog_ERR(TAG, "unable to retrieve bindings");
1110 break;
1111 }
1112
1113 free_tls_public_key(tls);
1114 if (!freerdp_certificate_get_public_key(cert, &tls->PublicKey, &tls->PublicKeyLength))
1115 {
1116 WLog_ERR(TAG,
1117 "freerdp_certificate_get_public_key failed to return the server public key.");
1118 break;
1119 }
1120
1121 /* server-side NLA needs public keys (keys from us, the server) but no certificate verify */
1122 ret = TLS_HANDSHAKE_SUCCESS;
1123
1124 if (tls->isClientMode)
1125 {
1126 WINPR_ASSERT(tls->port <= UINT16_MAX);
1127 verify_status =
1128 tls_verify_certificate(tls, cert, tls_get_server_name(tls), (UINT16)tls->port);
1129
1130 if (verify_status < 1)
1131 {
1132 WLog_ERR(TAG, "certificate not trusted, aborting.");
1133 freerdp_tls_send_alert(tls);
1134 ret = TLS_HANDSHAKE_VERIFY_ERROR;
1135 }
1136 }
1137 } while (0);
1138
1139 freerdp_certificate_free(cert);
1140 return ret;
1141}
1142
1143static int pollAndHandshake(rdpTls* tls)
1144{
1145 WINPR_ASSERT(tls);
1146
1147 do
1148 {
1149 HANDLE events[] = { freerdp_abort_event(tls->context), nullptr };
1150 DWORD status = 0;
1151 if (BIO_get_event(tls->bio, &events[1]) < 0)
1152 {
1153 WLog_ERR(TAG, "unable to retrieve BIO associated event");
1154 return -1;
1155 }
1156
1157 if (!events[1])
1158 {
1159 WLog_ERR(TAG, "unable to retrieve BIO event");
1160 return -1;
1161 }
1162
1163 status = WaitForMultipleObjectsEx(ARRAYSIZE(events), events, FALSE, INFINITE, TRUE);
1164 switch (status)
1165 {
1166 case WAIT_OBJECT_0 + 1:
1167 break;
1168 case WAIT_OBJECT_0:
1169 WLog_DBG(TAG, "Abort event set, cancel connect");
1170 return -1;
1171 case WAIT_TIMEOUT:
1172 case WAIT_IO_COMPLETION:
1173 continue;
1174 default:
1175 WLog_ERR(TAG, "error during WaitForSingleObject(): 0x%08" PRIX32 "", status);
1176 return -1;
1177 }
1178
1179 TlsHandshakeResult result = freerdp_tls_handshake(tls);
1180 switch (result)
1181 {
1182 case TLS_HANDSHAKE_CONTINUE:
1183 break;
1184 case TLS_HANDSHAKE_SUCCESS:
1185 return 1;
1186 case TLS_HANDSHAKE_ERROR:
1187 case TLS_HANDSHAKE_VERIFY_ERROR:
1188 default:
1189 return -1;
1190 }
1191 } while (TRUE);
1192}
1193
1194int freerdp_tls_connect(rdpTls* tls, BIO* underlying)
1195{
1196 const SSL_METHOD* method = freerdp_tls_get_ssl_method(FALSE, TRUE);
1197
1198 WINPR_ASSERT(tls);
1199 TlsHandshakeResult result = freerdp_tls_connect_ex(tls, underlying, method);
1200 switch (result)
1201 {
1202 case TLS_HANDSHAKE_SUCCESS:
1203 return 1;
1204 case TLS_HANDSHAKE_CONTINUE:
1205 break;
1206 case TLS_HANDSHAKE_ERROR:
1207 case TLS_HANDSHAKE_VERIFY_ERROR:
1208 return -1;
1209 default:
1210 return -1;
1211 }
1212
1213 return pollAndHandshake(tls);
1214}
1215
1216#if defined(MICROSOFT_IOS_SNI_BUG) && !defined(OPENSSL_NO_TLSEXT) && \
1217 !defined(LIBRESSL_VERSION_NUMBER)
1218static void tls_openssl_tlsext_debug_callback(SSL* s, int client_server, int type,
1219 unsigned char* data, int len, void* arg)
1220{
1221 if (type == TLSEXT_TYPE_server_name)
1222 {
1223 WLog_DBG(TAG, "Client uses SNI (extension disabled)");
1224 s->servername_done = 2;
1225 }
1226}
1227#endif
1228
1229BOOL freerdp_tls_accept(rdpTls* tls, BIO* underlying, rdpSettings* settings)
1230{
1231 WINPR_ASSERT(tls);
1232 TlsHandshakeResult res =
1233 freerdp_tls_accept_ex(tls, underlying, settings, freerdp_tls_get_ssl_method(FALSE, FALSE));
1234 switch (res)
1235 {
1236 case TLS_HANDSHAKE_SUCCESS:
1237 return TRUE;
1238 case TLS_HANDSHAKE_CONTINUE:
1239 break;
1240 case TLS_HANDSHAKE_ERROR:
1241 case TLS_HANDSHAKE_VERIFY_ERROR:
1242 default:
1243 return FALSE;
1244 }
1245
1246 return pollAndHandshake(tls) > 0;
1247}
1248
1249TlsHandshakeResult freerdp_tls_accept_ex(rdpTls* tls, BIO* underlying, rdpSettings* settings,
1250 const SSL_METHOD* methods)
1251{
1252 WINPR_ASSERT(tls);
1253
1254 int options = 0;
1255 int status = 0;
1256
1263 options |= SSL_OP_NO_SSLv2;
1273#ifdef SSL_OP_NO_COMPRESSION
1274 options |= SSL_OP_NO_COMPRESSION;
1275#endif
1282 options |= SSL_OP_TLS_BLOCK_PADDING_BUG;
1289 options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
1290
1297#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && (OPENSSL_VERSION_NUMBER < 0x30000000L) && \
1298 !defined(LIBRESSL_VERSION_NUMBER)
1299 options |= SSL_OP_NO_RENEGOTIATION;
1300#endif
1301
1302 if (!tls_prepare(tls, underlying, methods, options, FALSE))
1303 return TLS_HANDSHAKE_ERROR;
1304
1305 const rdpPrivateKey* key = freerdp_settings_get_pointer(settings, FreeRDP_RdpServerRsaKey);
1306 if (!key)
1307 {
1308 WLog_ERR(TAG, "invalid private key");
1309 return TLS_HANDSHAKE_ERROR;
1310 }
1311
1312 EVP_PKEY* privkey = freerdp_key_get_evp_pkey(key);
1313 if (!privkey)
1314 {
1315 WLog_ERR(TAG, "invalid private key");
1316 return TLS_HANDSHAKE_ERROR;
1317 }
1318
1319 status = SSL_use_PrivateKey(tls->ssl, privkey);
1320 /* The local reference to the private key will anyway go out of
1321 * scope; so the reference count should be decremented whether
1322 * SSL_use_PrivateKey succeeds or fails.
1323 */
1324 EVP_PKEY_free(privkey);
1325
1326 if (status <= 0)
1327 {
1328 WLog_ERR(TAG, "SSL_CTX_use_PrivateKey_file failed");
1329 return TLS_HANDSHAKE_ERROR;
1330 }
1331
1332 rdpCertificate* cert =
1333 freerdp_settings_get_pointer_writable(settings, FreeRDP_RdpServerCertificate);
1334 if (!cert)
1335 {
1336 WLog_ERR(TAG, "invalid certificate");
1337 return TLS_HANDSHAKE_ERROR;
1338 }
1339
1340 status = SSL_use_certificate(tls->ssl, freerdp_certificate_get_x509(cert));
1341
1342 if (status <= 0)
1343 {
1344 WLog_ERR(TAG, "SSL_use_certificate_file failed");
1345 return TLS_HANDSHAKE_ERROR;
1346 }
1347
1348 const size_t cnt = freerdp_certificate_get_chain_len(cert);
1349 for (size_t x = 0; x < cnt; x++)
1350 {
1351 X509* xcert = freerdp_certificate_get_chain_at(cert, x);
1352 WINPR_ASSERT(xcert);
1353 const long rc = SSL_add1_chain_cert(tls->ssl, xcert);
1354 if (rc != 1)
1355 {
1356 WLog_ERR(TAG, "SSL_add1_chain_cert failed");
1357 return TLS_HANDSHAKE_ERROR;
1358 }
1359 }
1360
1361#if defined(MICROSOFT_IOS_SNI_BUG) && !defined(OPENSSL_NO_TLSEXT) && \
1362 !defined(LIBRESSL_VERSION_NUMBER)
1363 SSL_set_tlsext_debug_callback(tls->ssl, tls_openssl_tlsext_debug_callback);
1364#endif
1365
1366 return freerdp_tls_handshake(tls);
1367}
1368
1369BOOL freerdp_tls_send_alert(rdpTls* tls)
1370{
1371 WINPR_ASSERT(tls);
1372
1373 if (!tls)
1374 return FALSE;
1375
1376 if (!tls->ssl)
1377 return TRUE;
1378
1383#if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
1384 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER <= 0x2080300fL))
1385
1386 if (tls->alertDescription != TLS_ALERT_DESCRIPTION_CLOSE_NOTIFY)
1387 {
1397 SSL_SESSION* ssl_session = SSL_get_session(tls->ssl);
1398 SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(tls->ssl);
1399 SSL_set_quiet_shutdown(tls->ssl, 1);
1400
1401 if ((tls->alertLevel == TLS_ALERT_LEVEL_FATAL) && (ssl_session))
1402 SSL_CTX_remove_session(ssl_ctx, ssl_session);
1403
1404 tls->ssl->s3->alert_dispatch = 1;
1405 tls->ssl->s3->send_alert[0] = tls->alertLevel;
1406 tls->ssl->s3->send_alert[1] = tls->alertDescription;
1407
1408 if (tls->ssl->s3->wbuf.left == 0)
1409 tls->ssl->method->ssl_dispatch_alert(tls->ssl);
1410 }
1411
1412#endif
1413 return TRUE;
1414}
1415
1416int freerdp_tls_write_all(rdpTls* tls, const BYTE* data, size_t length)
1417{
1418 WINPR_ASSERT(tls);
1419 size_t offset = 0;
1420 BIO* bio = tls->bio;
1421
1422 if (length > INT32_MAX)
1423 return -1;
1424
1425 while (offset < length)
1426 {
1427 ERR_clear_error();
1428 const int status = BIO_write(bio, &data[offset], (int)(length - offset));
1429
1430 if (status > 0)
1431 offset += (size_t)status;
1432 else
1433 {
1434 if (!BIO_should_retry(bio))
1435 return -1;
1436
1437 if (BIO_write_blocked(bio))
1438 {
1439 const long rc = BIO_wait_write(bio, 100);
1440 if (rc < 0)
1441 return -1;
1442 }
1443 else if (BIO_read_blocked(bio))
1444 return -2; /* Abort write, there is data that must be read */
1445 else
1446 USleep(100);
1447 }
1448 }
1449
1450 return (int)length;
1451}
1452
1453int freerdp_tls_set_alert_code(rdpTls* tls, int level, int description)
1454{
1455 WINPR_ASSERT(tls);
1456 tls->alertLevel = level;
1457 tls->alertDescription = description;
1458 return 0;
1459}
1460
1461WINPR_ATTR_NODISCARD
1462static BOOL tls_match_hostname(const char* pattern, const size_t pattern_length,
1463 const char* hostname)
1464{
1465 WINPR_ASSERT(hostname);
1466 WINPR_ASSERT(pattern || (pattern_length == 0));
1467
1468 const size_t hlen = strlen(hostname);
1469 if (hlen == pattern_length)
1470 {
1471 if (_strnicmp(hostname, pattern, pattern_length) == 0)
1472 return TRUE;
1473 }
1474
1475 if ((pattern_length > 2) && (pattern[0] == '*') && (pattern[1] == '.') &&
1476 (hlen >= pattern_length))
1477 {
1478 /* Ensure wildcard only matches foo.example.com and not foo.bar.example.com */
1479 const size_t prefixlen = hlen - pattern_length;
1480 for (size_t x = 0; x < prefixlen; x++)
1481 {
1482 char cur = hostname[x];
1483 if (cur == '.')
1484 return FALSE;
1485 }
1486
1487 /* Check the hostname ends with the domain */
1488 const char* check_hostname = &hostname[prefixlen + 1];
1489 return _strnicmp(check_hostname, &pattern[1], pattern_length - 1) == 0;
1490 }
1491
1492 return FALSE;
1493}
1494
1495WINPR_ATTR_NODISCARD
1496static BOOL is_redirected(rdpTls* tls)
1497{
1498 rdpSettings* settings = tls->context->settings;
1499
1500 if (settings->GatewayArmTransport)
1501 return TRUE;
1502
1503 if (LB_NOREDIRECT & settings->RedirectionFlags)
1504 return FALSE;
1505
1506 return settings->RedirectionFlags != 0;
1507}
1508
1509WINPR_ATTR_NODISCARD
1510static BOOL is_accepted(rdpTls* tls, const rdpCertificate* cert)
1511{
1512 WINPR_ASSERT(tls);
1513 WINPR_ASSERT(tls->context);
1514 WINPR_ASSERT(cert);
1515 rdpSettings* settings = tls->context->settings;
1516 WINPR_ASSERT(settings);
1517
1518 FreeRDP_Settings_Keys_String keyAccepted = FreeRDP_AcceptedCert;
1519 FreeRDP_Settings_Keys_UInt32 keyLength = FreeRDP_AcceptedCertLength;
1520
1521 if (tls->isGatewayTransport)
1522 {
1523 keyAccepted = FreeRDP_GatewayAcceptedCert;
1524 keyLength = FreeRDP_GatewayAcceptedCertLength;
1525 }
1526 else if (is_redirected(tls))
1527 {
1528 keyAccepted = FreeRDP_RedirectionAcceptedCert;
1529 keyLength = FreeRDP_RedirectionAcceptedCertLength;
1530 }
1531
1532 const char* AcceptedKey = freerdp_settings_get_string(settings, keyAccepted);
1533 const UINT32 AcceptedKeyLength = freerdp_settings_get_uint32(settings, keyLength);
1534
1535 if ((AcceptedKeyLength > 0) && AcceptedKey)
1536 {
1537 BOOL accepted = FALSE;
1538 size_t pemLength = 0;
1539 char* pem = freerdp_certificate_get_pem_ex(cert, &pemLength, FALSE);
1540 if (pem && (AcceptedKeyLength == pemLength))
1541 {
1542 if (memcmp(AcceptedKey, pem, AcceptedKeyLength) == 0)
1543 accepted = TRUE;
1544 }
1545 free(pem);
1546 if (accepted)
1547 return TRUE;
1548 }
1549
1550 if (!freerdp_settings_set_string(settings, keyAccepted, nullptr))
1551 WLog_WARN(TAG, "freerdp_settings_set_string(settings, keyAccepted=%d, nullptr) failed",
1552 keyAccepted);
1553 if (!freerdp_settings_set_uint32(settings, keyLength, 0))
1554 WLog_WARN(TAG, "freerdp_settings_set_uint32(settings, keyLength=%d, 0) failed", keyLength);
1555
1556 return FALSE;
1557}
1558
1559WINPR_ATTR_NODISCARD
1560static BOOL compare_fingerprint(const char* fp, const char* hash, const rdpCertificate* cert,
1561 BOOL separator)
1562{
1563 BOOL equal = 0;
1564 char* strhash = nullptr;
1565
1566 WINPR_ASSERT(fp);
1567 WINPR_ASSERT(hash);
1568 WINPR_ASSERT(cert);
1569
1570 strhash = freerdp_certificate_get_fingerprint_by_hash_ex(cert, hash, separator);
1571 if (!strhash)
1572 return FALSE;
1573
1574 equal = (_stricmp(strhash, fp) == 0);
1575 free(strhash);
1576 return equal;
1577}
1578
1579WINPR_ATTR_NODISCARD
1580static BOOL compare_fingerprint_all(const char* fp, const char* hash, const rdpCertificate* cert)
1581{
1582 WINPR_ASSERT(fp);
1583 WINPR_ASSERT(hash);
1584 WINPR_ASSERT(cert);
1585 if (compare_fingerprint(fp, hash, cert, FALSE))
1586 return TRUE;
1587 if (compare_fingerprint(fp, hash, cert, TRUE))
1588 return TRUE;
1589 return FALSE;
1590}
1591
1592WINPR_ATTR_NODISCARD
1593static BOOL is_accepted_fingerprint(const rdpCertificate* cert,
1594 const char* CertificateAcceptedFingerprints)
1595{
1596 WINPR_ASSERT(cert);
1597
1598 BOOL rc = FALSE;
1599 if (CertificateAcceptedFingerprints)
1600 {
1601 char* context = nullptr;
1602 char* copy = _strdup(CertificateAcceptedFingerprints);
1603 char* cur = strtok_s(copy, ",", &context);
1604 while (cur)
1605 {
1606 char* subcontext = nullptr;
1607 const char* h = strtok_s(cur, ":", &subcontext);
1608
1609 if (!h)
1610 goto next;
1611
1612 {
1613 const char* fp = h + strlen(h) + 1;
1614 if (compare_fingerprint_all(fp, h, cert))
1615 {
1616 rc = TRUE;
1617 break;
1618 }
1619 }
1620 next:
1621 cur = strtok_s(nullptr, ",", &context);
1622 }
1623 free(copy);
1624 }
1625
1626 return rc;
1627}
1628
1629WINPR_ATTR_NODISCARD
1630static BOOL accept_cert(rdpTls* tls, const rdpCertificate* cert)
1631{
1632 WINPR_ASSERT(tls);
1633 WINPR_ASSERT(tls->context);
1634 WINPR_ASSERT(cert);
1635
1636 FreeRDP_Settings_Keys_String id = FreeRDP_AcceptedCert;
1637 FreeRDP_Settings_Keys_UInt32 lid = FreeRDP_AcceptedCertLength;
1638
1639 rdpSettings* settings = tls->context->settings;
1640 WINPR_ASSERT(settings);
1641
1642 if (tls->isGatewayTransport)
1643 {
1644 id = FreeRDP_GatewayAcceptedCert;
1645 lid = FreeRDP_GatewayAcceptedCertLength;
1646 }
1647 else if (is_redirected(tls))
1648 {
1649 id = FreeRDP_RedirectionAcceptedCert;
1650 lid = FreeRDP_RedirectionAcceptedCertLength;
1651 }
1652
1653 size_t pemLength = 0;
1654 char* pem = freerdp_certificate_get_pem_ex(cert, &pemLength, FALSE);
1655 BOOL rc = FALSE;
1656 if (pemLength <= UINT32_MAX)
1657 {
1658 if (freerdp_settings_set_string_len(settings, id, pem, pemLength))
1659 rc = freerdp_settings_set_uint32(settings, lid, (UINT32)pemLength);
1660 }
1661 free(pem);
1662 return rc;
1663}
1664
1665WINPR_ATTR_NODISCARD
1666static BOOL tls_extract_full_pem(const rdpCertificate* cert, BYTE** PublicKey,
1667 size_t* PublicKeyLength)
1668{
1669 if (!cert || !PublicKey)
1670 return FALSE;
1671 *PublicKey = (BYTE*)freerdp_certificate_get_pem(cert, PublicKeyLength);
1672 return *PublicKey != nullptr;
1673}
1674
1675WINPR_ATTR_NODISCARD
1676static int tls_config_parse_bool(WINPR_JSON* json, const char* opt)
1677{
1678 WINPR_JSON* val = WINPR_JSON_GetObjectItemCaseSensitive(json, opt);
1679 if (!val || !WINPR_JSON_IsBool(val))
1680 return -1;
1681
1682 if (WINPR_JSON_IsTrue(val))
1683 return 1;
1684 return 0;
1685}
1686
1687WINPR_ATTR_NODISCARD
1688static int tls_config_check_allowed_hashed(const char* configfile, const rdpCertificate* cert,
1689 WINPR_JSON* json)
1690{
1691 WINPR_ASSERT(configfile);
1692 WINPR_ASSERT(cert);
1693 WINPR_ASSERT(json);
1694
1695 WINPR_JSON* db = WINPR_JSON_GetObjectItemCaseSensitive(json, "certificate-db");
1696 if (!db || !WINPR_JSON_IsArray(db))
1697 return 0;
1698
1699 for (size_t x = 0; x < WINPR_JSON_GetArraySize(db); x++)
1700 {
1701 WINPR_JSON* cur = WINPR_JSON_GetArrayItem(db, x);
1702 if (!cur || !WINPR_JSON_IsObject(cur))
1703 {
1704 WLog_WARN(TAG,
1705 "[%s] invalid certificate-db entry at position %" PRIuz ": not a JSON object",
1706 configfile, x);
1707 continue;
1708 }
1709
1710 WINPR_JSON* key = WINPR_JSON_GetObjectItemCaseSensitive(cur, "type");
1711 if (!key || !WINPR_JSON_IsString(key))
1712 {
1713 WLog_WARN(TAG,
1714 "[%s] invalid certificate-db entry at position %" PRIuz
1715 ": invalid 'type' element, expected type string",
1716 configfile, x);
1717 continue;
1718 }
1719 WINPR_JSON* val = WINPR_JSON_GetObjectItemCaseSensitive(cur, "hash");
1720 if (!val || !WINPR_JSON_IsString(val))
1721 {
1722 WLog_WARN(TAG,
1723 "[%s] invalid certificate-db entry at position %" PRIuz
1724 ": invalid 'hash' element, expected type string",
1725 configfile, x);
1726 continue;
1727 }
1728
1729 const char* skey = WINPR_JSON_GetStringValue(key);
1730 const char* sval = WINPR_JSON_GetStringValue(val);
1731
1732 char* hash = freerdp_certificate_get_fingerprint_by_hash_ex(cert, skey, FALSE);
1733 if (!hash)
1734 {
1735 WLog_WARN(TAG,
1736 "[%s] invalid certificate-db entry at position %" PRIuz
1737 ": hash type '%s' not supported by certificate",
1738 configfile, x, skey);
1739 continue;
1740 }
1741
1742 const int cmp = _stricmp(hash, sval);
1743 free(hash);
1744
1745 if (cmp == 0)
1746 return 1;
1747 }
1748
1749 return 0;
1750}
1751
1752WINPR_ATTR_NODISCARD
1753static int tls_config_check_certificate(const rdpCertificate* cert, BOOL* pAllowUserconfig)
1754{
1755 WINPR_ASSERT(cert);
1756 WINPR_ASSERT(pAllowUserconfig);
1757
1758 int rc = 0;
1759 const char configfile[] = "certificates.json";
1760 WINPR_JSON* json = freerdp_GetJSONConfigFile(TRUE, configfile);
1761
1762 if (!json)
1763 {
1764 WLog_DBG(TAG, "No or no valid configuration file for certificate handling, asking user");
1765 goto fail;
1766 }
1767
1768 if (tls_config_parse_bool(json, "deny") > 0)
1769 {
1770 WLog_WARN(TAG, "[%s] certificate denied by configuration", configfile);
1771 rc = -1;
1772 goto fail;
1773 }
1774
1775 if (tls_config_parse_bool(json, "ignore") > 0)
1776 {
1777 WLog_WARN(TAG, "[%s] certificate ignored by configuration", configfile);
1778 rc = 1;
1779 goto fail;
1780 }
1781
1782 if (tls_config_check_allowed_hashed(configfile, cert, json) > 0)
1783 {
1784 WLog_WARN(TAG, "[%s] certificate manually accepted by configuration", configfile);
1785 rc = 1;
1786 goto fail;
1787 }
1788
1789 if (tls_config_parse_bool(json, "deny-userconfig") > 0)
1790 {
1791 WLog_WARN(TAG, "[%s] configuration denies user to accept certificates", configfile);
1792 rc = -1;
1793 goto fail;
1794 }
1795
1796fail:
1797
1798 *pAllowUserconfig = (rc == 0);
1799 WINPR_JSON_Delete(json);
1800 return rc;
1801}
1802
1803int tls_verify_certificate(rdpTls* tls, const rdpCertificate* cert, const char* hostname,
1804 UINT16 port)
1805{
1806 int match = 0;
1807 size_t length = 0;
1808 BOOL certificate_status = 0;
1809 char* common_name = nullptr;
1810 size_t common_name_length = 0;
1811 char** dns_names = nullptr;
1812 size_t dns_names_count = 0;
1813 size_t* dns_names_lengths = nullptr;
1814 char** ip_names = nullptr;
1815 size_t ip_names_count = 0;
1816 size_t* ip_names_lengths = nullptr;
1817 int verification_status = -1;
1818 BOOL hostname_match = FALSE;
1819 rdpCertificateData* certificate_data = nullptr;
1820 BYTE* pemCert = nullptr;
1821 DWORD flags = VERIFY_CERT_FLAG_NONE;
1822
1823 WINPR_ASSERT(tls);
1824
1825 rdpContext* context = tls->context;
1826 WINPR_ASSERT(context);
1827
1828 freerdp* instance = context->instance;
1829 WINPR_ASSERT(instance);
1830
1831 const rdpSettings* settings = context->settings;
1832 WINPR_ASSERT(settings);
1833
1834 if (freerdp_shall_disconnect_context(context))
1835 return -1;
1836
1837 if (!tls_extract_full_pem(cert, &pemCert, &length))
1838 goto end;
1839
1840 /* Check, if we already accepted this key. */
1841 if (is_accepted(tls, cert))
1842 {
1843 verification_status = 1;
1844 goto end;
1845 }
1846
1847 if (is_accepted_fingerprint(cert, settings->CertificateAcceptedFingerprints))
1848 {
1849 verification_status = 1;
1850 goto end;
1851 }
1852
1853 if (tls->isGatewayTransport || is_redirected(tls))
1854 flags |= VERIFY_CERT_FLAG_LEGACY;
1855
1856 if (tls->isGatewayTransport)
1857 flags |= VERIFY_CERT_FLAG_GATEWAY;
1858
1859 if (is_redirected(tls))
1860 flags |= VERIFY_CERT_FLAG_REDIRECT;
1861
1862 /* Certificate management is done by the application */
1863 if (settings->ExternalCertificateManagement)
1864 {
1865 if (instance->VerifyX509Certificate)
1866 verification_status =
1867 instance->VerifyX509Certificate(instance, pemCert, length, hostname, port, flags);
1868 else
1869 WLog_ERR(TAG, "No VerifyX509Certificate callback registered!");
1870
1871 if (verification_status > 0)
1872 {
1873 if (!accept_cert(tls, cert))
1874 goto end;
1875 }
1876 else if (verification_status < 0)
1877 {
1878 WLog_ERR(TAG, "VerifyX509Certificate failed: (length = %" PRIuz ") status: [%d] %s",
1879 length, verification_status, pemCert);
1880 goto end;
1881 }
1882 }
1883 /* ignore certificate verification if user explicitly required it (discouraged) */
1884 else if (freerdp_settings_get_bool(settings, FreeRDP_IgnoreCertificate))
1885 {
1886 WLog_WARN(TAG, "[DANGER] Certificate not checked, /cert:ignore in use.");
1887 WLog_WARN(TAG, "[DANGER] This prevents MITM attacks from being detected!");
1888 WLog_WARN(TAG,
1889 "[DANGER] Avoid using this unless in a secure LAN (=no internet) environment");
1890 verification_status = 1; /* success! */
1891 }
1892 else if (!tls->isGatewayTransport && (settings->AuthenticationLevel == 0))
1893 verification_status = 1; /* success! */
1894 else
1895 {
1896 /* if user explicitly specified a certificate name, use it instead of the hostname */
1897 if (!tls->isGatewayTransport && settings->CertificateName)
1898 hostname = settings->CertificateName;
1899
1900 /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */
1901 certificate_status = freerdp_certificate_verify(
1902 cert, freerdp_certificate_store_get_certs_path(tls->certificate_store));
1903 /* verify certificate name match */
1904 certificate_data = freerdp_certificate_data_new(hostname, port, cert);
1905 if (!certificate_data)
1906 goto end;
1907 /* extra common name and alternative names */
1908 common_name = freerdp_certificate_get_common_name(cert, &common_name_length);
1909 dns_names = freerdp_certificate_get_dns_names(cert, &dns_names_count, &dns_names_lengths);
1910 ip_names = freerdp_certificate_get_ip_names(cert, &ip_names_count, &ip_names_lengths);
1911
1912 if (utils_is_valid_ip(hostname))
1913 {
1914 const size_t hostlen = strlen(hostname);
1915 if ((hostlen != 0) && ip_names && (ip_names_count > 0))
1916 {
1917
1918 for (size_t index = 0; index < ip_names_count; index++)
1919 {
1920 if (utils_compare_ip_strings(ip_names[index], ip_names_lengths[index], hostname,
1921 hostlen))
1922 {
1923 hostname_match = TRUE;
1924 break;
1925 }
1926 }
1927 }
1928 }
1929 /* compare against alternative names */
1930 else if (dns_names)
1931 {
1932 for (size_t index = 0; index < dns_names_count; index++)
1933 {
1934 if (tls_match_hostname(dns_names[index], dns_names_lengths[index], hostname))
1935 {
1936 hostname_match = TRUE;
1937 break;
1938 }
1939 }
1940 }
1941 /* compare against common name */
1942 else if (common_name)
1943 {
1944 if (tls_match_hostname(common_name, common_name_length, hostname))
1945 hostname_match = TRUE;
1946 }
1947
1948 /* if the certificate is valid and the certificate name matches, verification succeeds
1949 */
1950 if (certificate_status && hostname_match)
1951 verification_status = 1; /* success! */
1952
1953 if (!hostname_match)
1954 flags |= VERIFY_CERT_FLAG_MISMATCH;
1955
1956 BOOL allowUserconfig = TRUE;
1957 if (!certificate_status || !hostname_match)
1958 verification_status = tls_config_check_certificate(cert, &allowUserconfig);
1959
1960 /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for
1961 * manual verification */
1962 if (allowUserconfig && (!certificate_status || !hostname_match))
1963 {
1964 DWORD accept_certificate = 0;
1965 size_t pem_length = 0;
1966 char* issuer = freerdp_certificate_get_issuer(cert);
1967 char* subject = freerdp_certificate_get_subject(cert);
1968 char* pem = freerdp_certificate_get_pem(cert, &pem_length);
1969
1970 if (!pem)
1971 goto end;
1972
1973 /* search for matching entry in known_hosts file */
1974 match =
1975 freerdp_certificate_store_contains_data(tls->certificate_store, certificate_data);
1976
1977 if (match == 1)
1978 {
1979 /* no entry was found in known_hosts file, prompt user for manual verification
1980 */
1981 if (!hostname_match)
1982 tls_print_certificate_name_mismatch_error(hostname, port, common_name,
1983 dns_names, dns_names_count);
1984
1985 {
1986 const char* type = "";
1987 if (freerdp_settings_get_bool(settings, FreeRDP_AutoAcceptCertificate))
1988 type = "tofo (auto-accept)";
1989
1990 if (freerdp_settings_get_bool(settings, FreeRDP_AutoDenyCertificate))
1991 type = "strict (auto-deny)";
1992
1993 if (freerdp_settings_get_bool(settings, FreeRDP_IgnoreCertificate))
1994 type = "ignore (no check)";
1995
1996 char* efp = freerdp_certificate_get_fingerprint(cert);
1997 tls_print_new_certificate_warn(tls->certificate_store, hostname, port, type,
1998 efp);
1999 free(efp);
2000 }
2001
2002#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
2003 WINPR_PRAGMA_DIAG_PUSH
2004 WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
2005#endif
2006
2007 /* Automatically accept certificate on first use */
2008 if (settings->AutoAcceptCertificate)
2009 {
2010 WLog_INFO(TAG, "No certificate stored, automatically accepting.");
2011 accept_certificate = 1;
2012 }
2013 else if (settings->AutoDenyCertificate)
2014 {
2015 WLog_INFO(TAG, "No certificate stored, automatically denying.");
2016 accept_certificate = 0;
2017 }
2018 else if (instance->VerifyX509Certificate)
2019 {
2020 int rc = instance->VerifyX509Certificate(instance, pemCert, pem_length,
2021 hostname, port, flags);
2022
2023 if (rc == 1)
2024 accept_certificate = 1;
2025 else if (rc > 1)
2026 accept_certificate = 2;
2027 else
2028 accept_certificate = 0;
2029 }
2030 else if (instance->VerifyCertificateEx)
2031 {
2032 const BOOL use_pem =
2033 freerdp_settings_get_bool(settings, FreeRDP_CertificateCallbackPreferPEM);
2034 char* fp = nullptr;
2035 DWORD cflags = flags;
2036 if (use_pem)
2037 {
2038 cflags |= VERIFY_CERT_FLAG_FP_IS_PEM;
2039 fp = pem;
2040 }
2041 else
2042 fp = freerdp_certificate_get_fingerprint(cert);
2043 accept_certificate = instance->VerifyCertificateEx(
2044 instance, hostname, port, common_name, subject, issuer, fp, cflags);
2045 if (!use_pem)
2046 free(fp);
2047 }
2048#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
2049 else if (instance->VerifyCertificate)
2050 {
2051 char* fp = freerdp_certificate_get_fingerprint(cert);
2052
2053 WLog_WARN(TAG, "The VerifyCertificate callback is deprecated, migrate your "
2054 "application to VerifyCertificateEx");
2055 accept_certificate = instance->VerifyCertificate(instance, common_name, subject,
2056 issuer, fp, !hostname_match);
2057 free(fp);
2058 }
2059#endif
2060 }
2061 else if (match == -1)
2062 {
2063 rdpCertificateData* stored_data =
2064 freerdp_certificate_store_load_data(tls->certificate_store, hostname, port);
2065 /* entry was found in known_hosts file, but fingerprint does not match. ask user
2066 * to use it */
2067 {
2068 char* efp = freerdp_certificate_get_fingerprint(cert);
2069 tls_print_certificate_error(tls->certificate_store, stored_data, hostname, port,
2070 efp);
2071 free(efp);
2072 }
2073
2074 if (!stored_data)
2075 WLog_WARN(TAG, "Failed to get certificate entry for %s:%" PRIu16 "", hostname,
2076 port);
2077
2078 if (settings->AutoDenyCertificate)
2079 {
2080 WLog_INFO(TAG, "No certificate stored, automatically denying.");
2081 accept_certificate = 0;
2082 }
2083 else if (instance->VerifyX509Certificate)
2084 {
2085 const int rc =
2086 instance->VerifyX509Certificate(instance, pemCert, pem_length, hostname,
2087 port, flags | VERIFY_CERT_FLAG_CHANGED);
2088
2089 if (rc == 1)
2090 accept_certificate = 1;
2091 else if (rc > 1)
2092 accept_certificate = 2;
2093 else
2094 accept_certificate = 0;
2095 }
2096 else if (instance->VerifyChangedCertificateEx)
2097 {
2098 DWORD cflags = flags | VERIFY_CERT_FLAG_CHANGED;
2099 const char* old_subject = freerdp_certificate_data_get_subject(stored_data);
2100 const char* old_issuer = freerdp_certificate_data_get_issuer(stored_data);
2101 const char* old_fp = freerdp_certificate_data_get_fingerprint(stored_data);
2102 const char* old_pem = freerdp_certificate_data_get_pem(stored_data);
2103 const BOOL fpIsAllocated =
2104 !old_pem ||
2105 !freerdp_settings_get_bool(settings, FreeRDP_CertificateCallbackPreferPEM);
2106 char* fp = nullptr;
2107 if (!fpIsAllocated)
2108 {
2109 cflags |= VERIFY_CERT_FLAG_FP_IS_PEM;
2110 fp = pem;
2111 old_fp = old_pem;
2112 }
2113 else
2114 {
2115 fp = freerdp_certificate_get_fingerprint(cert);
2116 }
2117 accept_certificate = instance->VerifyChangedCertificateEx(
2118 instance, hostname, port, common_name, subject, issuer, fp, old_subject,
2119 old_issuer, old_fp, cflags);
2120 if (fpIsAllocated)
2121 free(fp);
2122 }
2123#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
2124 else if (instance->VerifyChangedCertificate)
2125 {
2126 char* fp = freerdp_certificate_get_fingerprint(cert);
2127 const char* old_subject = freerdp_certificate_data_get_subject(stored_data);
2128 const char* old_issuer = freerdp_certificate_data_get_issuer(stored_data);
2129 const char* old_fingerprint =
2130 freerdp_certificate_data_get_fingerprint(stored_data);
2131
2132 WLog_WARN(TAG, "The VerifyChangedCertificate callback is deprecated, migrate "
2133 "your application to VerifyChangedCertificateEx");
2134 accept_certificate = instance->VerifyChangedCertificate(
2135 instance, common_name, subject, issuer, fp, old_subject, old_issuer,
2136 old_fingerprint);
2137 free(fp);
2138 }
2139#endif
2140
2141 freerdp_certificate_data_free(stored_data);
2142 }
2143 else if (match == 0)
2144 accept_certificate = 2; /* success! */
2145
2146#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
2147 WINPR_PRAGMA_DIAG_POP
2148#endif
2149
2150 /* Save certificate or do a simple accept / reject */
2151 switch (accept_certificate)
2152 {
2153 case 1:
2154
2155 /* user accepted certificate, add entry in known_hosts file */
2156 verification_status = freerdp_certificate_store_save_data(
2157 tls->certificate_store, certificate_data)
2158 ? 1
2159 : -1;
2160 break;
2161
2162 case 2:
2163 /* user did accept temporary, do not add to known hosts file */
2164 verification_status = 1;
2165 break;
2166
2167 default:
2168 /* user did not accept, abort and do not add entry in known_hosts file */
2169 verification_status = -1; /* failure! */
2170 break;
2171 }
2172
2173 free(issuer);
2174 free(subject);
2175 free(pem);
2176 }
2177
2178 if (verification_status > 0)
2179 {
2180 if (!accept_cert(tls, cert))
2181 goto end;
2182 }
2183 }
2184
2185end:
2186 freerdp_certificate_data_free(certificate_data);
2187 free(common_name);
2188 freerdp_certificate_free_ip_names(ip_names_count, ip_names_lengths, ip_names);
2189 freerdp_certificate_free_dns_names(dns_names_count, dns_names_lengths, dns_names);
2190 free(pemCert);
2191 return verification_status;
2192}
2193
2194void tls_print_new_certificate_warn(rdpCertificateStore* store, const char* hostname, UINT16 port,
2195 const char* type, const char* fingerprint)
2196{
2197 char* path = freerdp_certificate_store_get_cert_path(store, hostname, port);
2198
2199 WLog_ERR(TAG, "The host key for %s:%" PRIu16 " has changed", hostname, port);
2200 WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
2201 WLog_ERR(TAG, "@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
2202 WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
2203 WLog_ERR(TAG, "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
2204 WLog_ERR(TAG, "Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
2205 WLog_ERR(TAG, "It is also possible that a host key has just been changed.");
2206 WLog_ERR(TAG, "The fingerprint for the host key sent by the remote host is %s", fingerprint);
2207 WLog_ERR(TAG, "Please contact your system administrator.");
2208 WLog_ERR(TAG, "Add correct host key in %s to get rid of this message.", path);
2209 WLog_ERR(TAG, "Host key for %s has changed and you have requested %s checking.", hostname,
2210 type);
2211 WLog_ERR(TAG, "Host key verification failed.");
2212
2213 free(path);
2214}
2215
2216void tls_print_certificate_error(rdpCertificateStore* store,
2217 WINPR_ATTR_UNUSED rdpCertificateData* stored_data,
2218 const char* hostname, UINT16 port, const char* fingerprint)
2219{
2220 char* path = freerdp_certificate_store_get_cert_path(store, hostname, port);
2221
2222 WLog_ERR(TAG, "New host key for %s:%" PRIu16, hostname, port);
2223 WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
2224 WLog_ERR(TAG, "@ WARNING: NEW HOST IDENTIFICATION! @");
2225 WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
2226
2227 WLog_ERR(TAG, "The fingerprint for the host key sent by the remote host is %s", fingerprint);
2228 WLog_ERR(TAG, "Please contact your system administrator.");
2229 WLog_ERR(TAG, "Add correct host key in %s to get rid of this message.", path);
2230
2231 free(path);
2232}
2233
2234void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port,
2235 const char* common_name, char** alt_names,
2236 size_t alt_names_count)
2237{
2238 WINPR_ASSERT(nullptr != hostname);
2239 WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
2240 WLog_ERR(TAG, "@ WARNING: CERTIFICATE NAME MISMATCH! @");
2241 WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
2242 WLog_ERR(TAG, "The hostname used for this connection (%s:%" PRIu16 ") ", hostname, port);
2243 WLog_ERR(TAG, "does not match %s given in the certificate:",
2244 alt_names_count < 1 ? "the name" : "any of the names");
2245 WLog_ERR(TAG, "Common Name (CN):");
2246 WLog_ERR(TAG, "\t%s", common_name ? common_name : "no CN found in certificate");
2247
2248 if (alt_names_count > 0)
2249 {
2250 WINPR_ASSERT(nullptr != alt_names);
2251 WLog_ERR(TAG, "Alternative names:");
2252
2253 for (size_t index = 0; index < alt_names_count; index++)
2254 {
2255 WINPR_ASSERT(alt_names[index]);
2256 WLog_ERR(TAG, "\t %s", alt_names[index]);
2257 }
2258 }
2259
2260 WLog_ERR(TAG, "A valid certificate for the wrong name should NOT be trusted!");
2261}
2262
2263rdpTls* freerdp_tls_new(rdpContext* context)
2264{
2265 rdpTls* tls = nullptr;
2266 tls = (rdpTls*)calloc(1, sizeof(rdpTls));
2267
2268 if (!tls)
2269 return nullptr;
2270
2271 tls->context = context;
2272
2273 if (!freerdp_settings_get_bool(tls->context->settings, FreeRDP_ServerMode))
2274 {
2275 tls->certificate_store = freerdp_certificate_store_new(tls->context->settings);
2276
2277 if (!tls->certificate_store)
2278 goto out_free;
2279 }
2280
2281 tls->alertLevel = TLS_ALERT_LEVEL_WARNING;
2282 tls->alertDescription = TLS_ALERT_DESCRIPTION_CLOSE_NOTIFY;
2283 return tls;
2284out_free:
2285 free(tls);
2286 return nullptr;
2287}
2288
2289void freerdp_tls_free(rdpTls* tls)
2290{
2291 if (!tls)
2292 return;
2293
2294 tls_reset(tls);
2295
2296 if (tls->certificate_store)
2297 {
2298 freerdp_certificate_store_free(tls->certificate_store);
2299 tls->certificate_store = nullptr;
2300 }
2301
2302 free(tls);
2303}
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsBool(const WINPR_JSON *item)
Check if JSON item is of type BOOL.
Definition c-json.c:167
WINPR_ATTR_NODISCARD WINPR_API size_t WINPR_JSON_GetArraySize(const WINPR_JSON *array)
Get the number of arrayitems from an array.
Definition c-json.c:114
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsTrue(const WINPR_JSON *item)
Check if JSON item is BOOL value True.
Definition c-json.c:162
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON *object, const char *string)
Same as WINPR_JSON_GetObjectItem but with case sensitive matching.
Definition c-json.c:127
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
Definition c-json.c:182
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetArrayItem(const WINPR_JSON *array, size_t index)
Return a pointer to an item in the array.
Definition c-json.c:108
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsArray(const WINPR_JSON *item)
Check if JSON item is of type Array.
Definition c-json.c:187
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsObject(const WINPR_JSON *item)
Check if JSON item is of type Object.
Definition c-json.c:192
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition c-json.c:103
WINPR_ATTR_NODISCARD WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
Definition c-json.c:142
WINPR_ATTR_NODISCARD FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
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_string_len(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val, size_t len)
Sets a string settings value. The val is copied.
WINPR_ATTR_NODISCARD FREERDP_API void * freerdp_settings_get_pointer_writable(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a mutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 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_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.