FreeRDP
Loading...
Searching...
No Matches
rdpemt.c
1
18#include <freerdp/config.h>
19
20#include <string.h>
21
22#include <winpr/assert.h>
23#include <winpr/cast.h>
24#include <winpr/crt.h>
25#include <winpr/interlocked.h>
26#include <winpr/synch.h>
27#include <winpr/thread.h>
28#include <winpr/sysinfo.h>
29#include <winpr/winsock.h>
30#include <winpr/stream.h>
31
32#if !defined(_WIN32)
33#if defined(WINPR_HAVE_POLL_H)
34#include <poll.h>
35#else
36#include <sys/select.h>
37#endif
38#endif
39
40#include <openssl/ssl.h>
41#include <openssl/err.h>
42#include <openssl/bio.h>
43#include <openssl/x509.h>
44
45#include <freerdp/crypto/certificate.h>
46
47#include "../crypto/certificate.h"
48#include "rdpudp.h"
49#include "rdpemt.h"
50
51/* [MS-RDPEMT] 2.2.1.1 RDP_TUNNEL_HEADER Action */
52#define RDPTUNNEL_ACTION_CREATEREQUEST 0x0
53#define RDPTUNNEL_ACTION_CREATERESPONSE 0x1
54#define RDPTUNNEL_ACTION_DATA 0x2
55
56#define RDPTUNNEL_HEADER_LENGTH 4
57
58/* [MS-RDPEMT] 2.2.1.1.1 RDP_TUNNEL_SUBHEADER SubHeaderType */
59#define RDPTUNNEL_SUBHEADER_AUTODETECT_REQUEST 0x00
60
61/* Time allowed from the first SYN until the server accepts the tunnel. */
62#define RDPEMT_SETUP_TIMEOUT_MS 20000
63
64/* TLS record header: type, version, length */
65#define TLS_RECORD_HEADER_LENGTH 5
66
67/* Room per datagram for the TLS record header, padding and authentication tag. */
68#define TLS_RECORD_OVERHEAD 64
69
70struct rdp_emt
71{
72 wLog* log;
74 HANDLE thread;
75 LONG stop; /* set by rdpemt_free, read by the thread; Interlocked functions only */
76
77 UINT32 requestId;
78 BYTE cookie[16];
79 char* hostname;
80 BYTE* publicKey;
81 size_t publicKeyLength;
82
83 rdpEmtEventFn callback;
84 void* custom;
85
86 /* guarded by lock: tunnel data goes to receiver instead of callback when it is set */
87 rdpEmtReceiverFn receiver;
88 void* receiverCustom;
89 /* handed to receiver and not yet taken; Interlocked functions only */
90 LONG directBytes;
91
92 rdpUdp* udp;
93 SSL_CTX* ctx;
94 SSL* ssl;
95 BIO* rbio;
96 BIO* wbio;
97
98 BOOL tlsStarted;
99 BOOL handshakeDone;
100 BOOL ready;
101 BOOL failed;
102 UINT64 started;
103
104 /* TLS output not yet on the connection, and plaintext not yet parsed into tunnel PDUs;
105 * the position of each is how much they hold */
106 wStream* out;
107 wStream* in;
108};
109
110/* Drops the first count bytes of a stream that is filled up to its position. */
111WINPR_ATTR_NODISCARD
112static BOOL stream_consume(wStream* s, size_t count)
113{
114 WINPR_ASSERT(s);
115
116 const size_t used = Stream_GetPosition(s);
117 if (count > used)
118 return FALSE;
119 BYTE* buffer = Stream_Buffer(s);
120 memmove(buffer, &buffer[count], used - count);
121 return Stream_SetPosition(s, used - count);
122}
123
124static void emt_fail(rdpEmt* emt, const char* why)
125{
126 WINPR_ASSERT(emt);
127 WINPR_ASSERT(why);
128
129 if (emt->failed)
130 return;
131 emt->failed = TRUE;
132 emt->ready = FALSE;
133 WLog_Print(emt->log, WLOG_WARN, "UDP multitransport tunnel failed: %s", why);
134 emt->callback(emt->custom, emt, RDPEMT_EVENT_FAILED, nullptr, 0);
135}
136
137/* Called with lock held. */
138static void emt_hand_up(rdpEmt* emt, const BYTE* data, size_t length)
139{
140 WINPR_ASSERT(emt);
141 WINPR_ASSERT(data);
142
143 if (!emt->receiver)
144 {
145 emt->callback(emt->custom, emt, RDPEMT_EVENT_DATA, data, length);
146 return;
147 }
148
149 /* the receiver takes this stream over, the data is not copied again on the way */
150 wStream* s = Stream_New(nullptr, length);
151 if (!s)
152 {
153 emt_fail(emt, "out of memory for tunnel data");
154 return;
155 }
156 Stream_Write(s, data, length);
157 Stream_SealLength(s);
158 Stream_ResetPosition(s);
159 if (!emt->receiver(emt->receiverCustom, s))
160 {
161 emt_fail(emt, "the receiver of tunnel data failed");
162 return;
163 }
164 const LONG direct =
165 InterlockedExchangeAdd(&emt->directBytes, WINPR_ASSERTING_INT_CAST(LONG, length));
166 WINPR_UNUSED(direct);
167}
168
169static void log_ssl_errors(rdpEmt* emt, const char* what)
170{
171 WINPR_ASSERT(emt);
172 WINPR_ASSERT(what);
173
174 unsigned long err = 0;
175 while ((err = ERR_get_error()) != 0)
176 {
177 char buffer[256] = WINPR_C_ARRAY_INIT;
178 ERR_error_string_n(err, buffer, sizeof(buffer));
179 WLog_Print(emt->log, WLOG_WARN, "%s: %s", what, buffer);
180 }
181}
182
183/* Moves whatever TLS produced onto the RDP-UDP connection. Records are packed whole into
184 * datagrams, so a peer that reads one record per datagram works as well as one that reads the
185 * connection as a stream. */
186WINPR_ATTR_NODISCARD
187static BOOL emt_flush_tls(rdpEmt* emt)
188{
189 WINPR_ASSERT(emt);
190
191 while (TRUE)
192 {
193 const size_t pending = BIO_ctrl_pending(emt->wbio);
194 if (pending == 0)
195 break;
196 if (pending > INT32_MAX)
197 return FALSE;
198 if (!Stream_EnsureRemainingCapacity(emt->out, pending))
199 return FALSE;
200 const int rc =
201 BIO_read(emt->wbio, Stream_Pointer(emt->out), WINPR_ASSERTING_INT_CAST(int, pending));
202 if (rc <= 0)
203 break;
204 Stream_Seek(emt->out, WINPR_ASSERTING_INT_CAST(size_t, rc));
205 }
206
207 const size_t max = rdpudp_get_max_payload(emt->udp);
208 if (max == 0)
209 return FALSE;
210 const BYTE* data = Stream_Buffer(emt->out);
211 wStream sbuffer = WINPR_C_ARRAY_INIT;
212 wStream* s = Stream_StaticConstInit(&sbuffer, data, Stream_GetPosition(emt->out));
213
214 size_t chunk = 0;
215 while (Stream_GetRemainingLength(s) >= TLS_RECORD_HEADER_LENGTH)
216 {
217 /* TLS record header: ContentType, ProtocolVersion, length */
218 const size_t pos = Stream_GetPosition(s);
219 Stream_Seek(s, 3);
220 const size_t record = TLS_RECORD_HEADER_LENGTH + Stream_Get_UINT16_BE(s);
221 if (Stream_GetRemainingLength(s) < record - TLS_RECORD_HEADER_LENGTH)
222 {
223 if (!Stream_SetPosition(s, pos))
224 return FALSE;
225 break;
226 }
227 Stream_Seek(s, record - TLS_RECORD_HEADER_LENGTH);
228
229 if ((pos > chunk) && (pos + record - chunk > max))
230 {
231 if (!rdpudp_send(emt->udp, &data[chunk], pos - chunk))
232 return FALSE;
233 chunk = pos;
234 }
235
236 if (record > max)
237 {
238 /* should not happen with max_send_fragment set, the connection is a byte stream so
239 * a record split over datagrams still arrives intact */
240 for (size_t offset = 0; offset < record; offset += max)
241 {
242 const size_t length = (record - offset > max) ? max : record - offset;
243 if (!rdpudp_send(emt->udp, &data[pos + offset], length))
244 return FALSE;
245 }
246 chunk = pos + record;
247 }
248 }
249
250 const size_t end = Stream_GetPosition(s);
251 if ((end > chunk) && !rdpudp_send(emt->udp, &data[chunk], end - chunk))
252 return FALSE;
253
254 return stream_consume(emt->out, end);
255}
256
257WINPR_ATTR_NODISCARD
258static BOOL emt_write_pdu(rdpEmt* emt, BYTE action, const BYTE* subHeader, size_t subHeaderLength,
259 const BYTE* payload, size_t length)
260{
261 WINPR_ASSERT(emt);
262 WINPR_ASSERT(subHeader || (subHeaderLength == 0));
263 WINPR_ASSERT(payload || (length == 0));
264
265 if ((length > UINT16_MAX) || (subHeaderLength > UINT8_MAX - RDPTUNNEL_HEADER_LENGTH))
266 return FALSE;
267
268 wStream* s = Stream_New(nullptr, RDPTUNNEL_HEADER_LENGTH + subHeaderLength + length);
269 if (!s)
270 return FALSE;
271
272 /* [MS-RDPEMT] 2.2.1.1 RDP_TUNNEL_HEADER, HeaderLength includes the SubHeaders */
273 const UINT8 actionByte = WINPR_ASSERTING_INT_CAST(UINT8, action & 0x0F);
274 const UINT16 payloadLength = WINPR_ASSERTING_INT_CAST(UINT16, length);
275 const UINT8 headerLength =
276 WINPR_ASSERTING_INT_CAST(UINT8, RDPTUNNEL_HEADER_LENGTH + subHeaderLength);
277 Stream_Write_UINT8(s, actionByte); /* Action (4 bits), Flags (4 bits) */
278 Stream_Write_UINT16(s, payloadLength); /* PayloadLength */
279 Stream_Write_UINT8(s, headerLength); /* HeaderLength */
280 Stream_Write(s, subHeader, subHeaderLength);
281 Stream_Write(s, payload, length);
282
283 const int total = WINPR_ASSERTING_INT_CAST(int, Stream_GetPosition(s));
284 const int rc = SSL_write(emt->ssl, Stream_Buffer(s), total);
285 Stream_Free(s, TRUE);
286 if (rc != total)
287 {
288 log_ssl_errors(emt, "SSL_write");
289 return FALSE;
290 }
291 return emt_flush_tls(emt);
292}
293
294WINPR_ATTR_NODISCARD
295static BOOL emt_send_create_request(rdpEmt* emt)
296{
297 WINPR_ASSERT(emt);
298
299 BYTE request[24] = WINPR_C_ARRAY_INIT;
300 wStream sbuffer = WINPR_C_ARRAY_INIT;
301 wStream* s = Stream_StaticInit(&sbuffer, request, sizeof(request));
302
303 /* [MS-RDPEMT] 2.2.2.1 RDP_TUNNEL_CREATEREQUEST */
304 Stream_Write_UINT32(s, emt->requestId); /* RequestID */
305 Stream_Write_UINT32(s, 0); /* Reserved */
306 Stream_Write(s, emt->cookie, sizeof(emt->cookie)); /* SecurityCookie */
307
308 WLog_Print(emt->log, WLOG_DEBUG, "sending Tunnel Create Request for request id %" PRIu32,
309 emt->requestId);
310 return emt_write_pdu(emt, RDPTUNNEL_ACTION_CREATEREQUEST, nullptr, 0, Stream_Buffer(s),
311 Stream_GetPosition(s));
312}
313
314/* The TLS session inside the tunnel ends at the same server as the main connection, so it has
315 * to present the certificate that one was accepted with. */
316WINPR_ATTR_NODISCARD
317static BOOL emt_verify_peer(rdpEmt* emt)
318{
319 WINPR_ASSERT(emt);
320
321 X509* x509 = SSL_get_peer_certificate(emt->ssl);
322 if (!x509)
323 {
324 WLog_Print(emt->log, WLOG_WARN, "the tunnel's TLS session has no server certificate");
325 return FALSE;
326 }
327
328 BOOL rc = FALSE;
329 BYTE* key = nullptr;
330 DWORD keyLength = 0;
331 rdpCertificate* cert = freerdp_certificate_new_from_x509(x509, nullptr);
332 if (cert && freerdp_certificate_get_public_key(cert, &key, &keyLength))
333 {
334 rc = (keyLength == emt->publicKeyLength) && (memcmp(key, emt->publicKey, keyLength) == 0);
335 }
336 if (!rc)
337 WLog_Print(emt->log, WLOG_WARN,
338 "the tunnel's TLS certificate differs from the main connection's");
339
340 free(key);
341 freerdp_certificate_free(cert);
342 X509_free(x509);
343 return rc;
344}
345
346/* [MS-RDPEMT] 2.2.1.1.1: the SubHeaders field holds any number of RDP_TUNNEL_SUBHEADER. An
347 * auto-detect request is a whole subheader, its headerLength and headerTypeId are the
348 * SubHeaderLength and SubHeaderType ([MS-RDPBCGR] 2.2.14.1). */
349WINPR_ATTR_NODISCARD
350static BOOL emt_process_subheaders(rdpEmt* emt, const BYTE* data, size_t length)
351{
352 WINPR_ASSERT(emt);
353 WINPR_ASSERT(data || (length == 0));
354
355 while (length > 0)
356 {
357 const size_t subHeaderLength = data[0];
358 if ((length < 2) || (subHeaderLength < 2) || (subHeaderLength > length))
359 {
360 emt_fail(emt, "invalid tunnel subheader");
361 return FALSE;
362 }
363
364 const BYTE subHeaderType = data[1];
365 if (subHeaderType == RDPTUNNEL_SUBHEADER_AUTODETECT_REQUEST)
366 emt->callback(emt->custom, emt, RDPEMT_EVENT_AUTODETECT_REQUEST, data, subHeaderLength);
367 else
368 WLog_Print(emt->log, WLOG_DEBUG, "ignoring tunnel subheader of type 0x%02" PRIx8,
369 subHeaderType);
370 data += subHeaderLength;
371 length -= subHeaderLength;
372 }
373 return TRUE;
374}
375
376WINPR_ATTR_NODISCARD
377static BOOL emt_process_pdus(rdpEmt* emt)
378{
379 WINPR_ASSERT(emt);
380
381 wStream sbuffer = WINPR_C_ARRAY_INIT;
382 wStream* s =
383 Stream_StaticConstInit(&sbuffer, Stream_Buffer(emt->in), Stream_GetPosition(emt->in));
384
385 while (Stream_GetRemainingLength(s) >= RDPTUNNEL_HEADER_LENGTH)
386 {
387 /* [MS-RDPEMT] 2.2.1.1 RDP_TUNNEL_HEADER */
388 const size_t start = Stream_GetPosition(s);
389 const BYTE action = Stream_Get_UINT8(s) & 0x0F;
390 const size_t payloadLength = Stream_Get_UINT16(s);
391 const size_t headerLength = Stream_Get_UINT8(s);
392 if (headerLength < RDPTUNNEL_HEADER_LENGTH)
393 {
394 emt_fail(emt, "invalid tunnel PDU header");
395 return FALSE;
396 }
397
398 /* HeaderLength includes any subheaders, PayloadLength does not. Both are bounded by
399 * their wire types (UINT8, UINT16), the sum cannot overflow. */
400 WINPR_ASSERT(payloadLength <= UINT16_MAX);
401 WINPR_ASSERT(headerLength <= UINT8_MAX);
402 const size_t rest = headerLength - RDPTUNNEL_HEADER_LENGTH + payloadLength;
403 if (Stream_GetRemainingLength(s) < rest)
404 {
405 /* not all here yet */
406 if (!Stream_SetPosition(s, start))
407 return FALSE;
408 break;
409 }
410 /* the subheaders go up ahead of the payload of their PDU */
411 if (!emt_process_subheaders(emt, Stream_ConstPointer(s),
412 headerLength - RDPTUNNEL_HEADER_LENGTH))
413 return FALSE;
414 Stream_Seek(s, headerLength - RDPTUNNEL_HEADER_LENGTH);
415
416 switch (action)
417 {
418 case RDPTUNNEL_ACTION_CREATERESPONSE:
419 {
420 /* [MS-RDPEMT] 2.2.2.2 RDP_TUNNEL_CREATERESPONSE */
421 if (payloadLength < 4)
422 {
423 emt_fail(emt, "short Tunnel Create Response");
424 return FALSE;
425 }
426 const UINT32 hr = Stream_Get_UINT32(s); /* HrResponse */
427 Stream_Seek(s, payloadLength - 4);
428 if (FAILED((HRESULT)hr))
429 {
430 WLog_Print(emt->log, WLOG_WARN,
431 "the server refused the tunnel, hrResponse 0x%08" PRIx32, hr);
432 emt_fail(emt, "Tunnel Create Response reported a failure");
433 return FALSE;
434 }
435 if (!emt->ready)
436 {
437 WLog_Print(emt->log, WLOG_INFO,
438 "UDP multitransport tunnel ready (RDP-UDP version 0x%04" PRIx16 ")",
439 rdpudp_get_version(emt->udp));
440 emt->ready = TRUE;
441 emt->callback(emt->custom, emt, RDPEMT_EVENT_READY, nullptr, 0);
442 }
443 }
444 break;
445
446 case RDPTUNNEL_ACTION_DATA:
447 /* [MS-RDPEMT] 2.2.2.3 RDP_TUNNEL_DATA, HigherLayerData is a DVC PDU */
448 if (payloadLength > 0)
449 emt_hand_up(emt, Stream_ConstPointer(s), payloadLength);
450 Stream_Seek(s, payloadLength);
451 break;
452
453 default:
454 WLog_Print(emt->log, WLOG_DEBUG, "ignoring tunnel PDU with action %" PRIu8, action);
455 Stream_Seek(s, payloadLength);
456 break;
457 }
458
459 if (emt->failed)
460 return FALSE;
461 }
462
463 return stream_consume(emt->in, Stream_GetPosition(s));
464}
465
466WINPR_ATTR_NODISCARD
467static BOOL emt_run_tls(rdpEmt* emt)
468{
469 WINPR_ASSERT(emt);
470
471 if (!emt->handshakeDone)
472 {
473 ERR_clear_error();
474 const int rc = SSL_do_handshake(emt->ssl);
475 if (!emt_flush_tls(emt))
476 return FALSE;
477 if (rc != 1)
478 {
479 const int err = SSL_get_error(emt->ssl, rc);
480 if ((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE))
481 return TRUE;
482 log_ssl_errors(emt, "TLS handshake");
483 emt_fail(emt, "TLS handshake failed");
484 return FALSE;
485 }
486
487 emt->handshakeDone = TRUE;
488 WLog_Print(emt->log, WLOG_DEBUG, "tunnel TLS session established (%s, %s)",
489 SSL_get_version(emt->ssl), SSL_get_cipher_name(emt->ssl));
490 if (!emt_verify_peer(emt))
491 {
492 emt_fail(emt, "certificate mismatch");
493 return FALSE;
494 }
495 if (!emt_send_create_request(emt))
496 {
497 emt_fail(emt, "failed to send the Tunnel Create Request");
498 return FALSE;
499 }
500 }
501
502 while (TRUE)
503 {
504 if (!Stream_EnsureRemainingCapacity(emt->in, 16384))
505 return FALSE;
506
507 ERR_clear_error();
508 const int rc = SSL_read(emt->ssl, Stream_Pointer(emt->in), 16384);
509 if (rc > 0)
510 {
511 Stream_Seek(emt->in, WINPR_ASSERTING_INT_CAST(size_t, rc));
512 continue;
513 }
514
515 const int err = SSL_get_error(emt->ssl, rc);
516 if (err == SSL_ERROR_WANT_READ)
517 break;
518 log_ssl_errors(emt, "SSL_read");
519 emt_fail(emt, (err == SSL_ERROR_ZERO_RETURN) ? "the server closed the tunnel"
520 : "TLS error inside the tunnel");
521 return FALSE;
522 }
523
524 /* post handshake messages and alerts */
525 if (!emt_flush_tls(emt))
526 return FALSE;
527
528 return emt_process_pdus(emt);
529}
530
531WINPR_ATTR_NODISCARD
532static BOOL emt_receive(void* custom, const BYTE* data, size_t length)
533{
534 rdpEmt* emt = custom;
535 WINPR_ASSERT(emt);
536 WINPR_ASSERT(data || (length == 0));
537
538 if (!emt->tlsStarted || emt->failed)
539 return TRUE;
540 if (length > INT32_MAX)
541 return FALSE;
542 const int len = WINPR_ASSERTING_INT_CAST(int, length);
543 if (BIO_write(emt->rbio, data, len) != len)
544 return FALSE;
545 return emt_run_tls(emt);
546}
547
548WINPR_ATTR_NODISCARD
549static BOOL emt_start_tls(rdpEmt* emt)
550{
551 WINPR_ASSERT(emt);
552
553 emt->ctx = SSL_CTX_new(TLS_client_method());
554 if (!emt->ctx)
555 return FALSE;
556
557 if (!SSL_CTX_set_min_proto_version(emt->ctx, TLS1_2_VERSION))
558 {
559 WLog_Print(emt->log, WLOG_ERROR, "SSL_CTX_set_min_proto_version failed");
560 return FALSE;
561 }
562 SSL_CTX_set_options(emt->ctx, SSL_OP_NO_TICKET | SSL_OP_NO_COMPRESSION);
563 /* the certificate is checked against the main connection's in emt_verify_peer */
564 SSL_CTX_set_verify(emt->ctx, SSL_VERIFY_NONE, nullptr);
565 if (!SSL_CTX_set_cipher_list(emt->ctx, "ECDHE-RSA-AES256-GCM-SHA384:"
566 "ECDHE-RSA-AES128-GCM-SHA256:"
567 "ECDHE-ECDSA-AES256-GCM-SHA384:"
568 "ECDHE-ECDSA-AES128-GCM-SHA256:"
569 "AES256-GCM-SHA384:AES128-GCM-SHA256"))
570 {
571 WLog_Print(emt->log, WLOG_ERROR, "SSL_CTX_set_cipher_list failed");
572 return FALSE;
573 }
574#if OPENSSL_VERSION_NUMBER >= 0x10101000L
575 if (!SSL_CTX_set_ciphersuites(emt->ctx, "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"))
576 {
577 WLog_Print(emt->log, WLOG_ERROR, "SSL_CTX_set_ciphersuites failed");
578 return FALSE;
579 }
580#endif
581 /* The ClientHello has to fit in one datagram. Default groups with post quantum key shares
582 * add more than a kilobyte. */
583 if (!SSL_CTX_set1_groups_list(emt->ctx, "X25519:P-256:P-384"))
584 {
585 WLog_Print(emt->log, WLOG_ERROR, "SSL_CTX_set1_groups_list failed");
586 return FALSE;
587 }
588 if (!SSL_CTX_set1_sigalgs_list(emt->ctx, "rsa_pss_rsae_sha256:rsa_pss_rsae_sha384:"
589 "rsa_pkcs1_sha256:rsa_pkcs1_sha384:"
590 "ecdsa_secp256r1_sha256:ecdsa_secp384r1_sha384"))
591 {
592 WLog_Print(emt->log, WLOG_ERROR, "SSL_CTX_set1_sigalgs_list failed");
593 return FALSE;
594 }
595 SSL_CTX_set_read_ahead(emt->ctx, 0);
596
597 emt->ssl = SSL_new(emt->ctx);
598 if (!emt->ssl)
599 return FALSE;
600
601 emt->rbio = BIO_new(BIO_s_mem());
602 emt->wbio = BIO_new(BIO_s_mem());
603 if (!emt->rbio || !emt->wbio)
604 {
605 BIO_free(emt->rbio);
606 BIO_free(emt->wbio);
607 emt->rbio = nullptr;
608 emt->wbio = nullptr;
609 return FALSE;
610 }
611 SSL_set_bio(emt->ssl, emt->rbio, emt->wbio);
612 SSL_set_connect_state(emt->ssl);
613 if (emt->hostname)
614 (void)SSL_set_tlsext_host_name(emt->ssl, emt->hostname);
615
616 /* every record fits in one datagram */
617 const size_t max = rdpudp_get_max_payload(emt->udp);
618 if (max > TLS_RECORD_OVERHEAD + 512)
619 {
620 if (!SSL_set_max_send_fragment(emt->ssl,
621 WINPR_ASSERTING_INT_CAST(long, max - TLS_RECORD_OVERHEAD)))
622 {
623 WLog_Print(emt->log, WLOG_ERROR, "SSL_set_max_send_fragment failed");
624 return FALSE;
625 }
626 }
627
628 emt->tlsStarted = TRUE;
629 return emt_run_tls(emt);
630}
631
632WINPR_ATTR_NODISCARD
633static BOOL emt_poll(rdpEmt* emt)
634{
635 WINPR_ASSERT(emt);
636
637 if (emt->failed)
638 return FALSE;
639
640 if (!rdpudp_check(emt->udp))
641 {
642 emt_fail(emt, "the RDP-UDP connection failed");
643 return FALSE;
644 }
645
646 if (!emt->tlsStarted && (rdpudp_get_state(emt->udp) == RDPUDP_STATE_ESTABLISHED))
647 {
648 if (!emt_start_tls(emt))
649 {
650 log_ssl_errors(emt, "TLS setup");
651 emt_fail(emt, "failed to start TLS");
652 return FALSE;
653 }
654 }
655
656 if (!emt->ready && (GetTickCount64() - emt->started > RDPEMT_SETUP_TIMEOUT_MS))
657 {
658 emt_fail(emt, "timed out");
659 return FALSE;
660 }
661
662 return !emt->failed;
663}
664
665/* Waits until a datagram arrives or the timeout in milliseconds passes. Waking up early, or
666 * from an interrupted wait, only makes the timers run sooner. */
667static void emt_wait_readable(SOCKET sockfd, UINT32 timeout)
668{
669#if !defined(_WIN32) && defined(WINPR_HAVE_POLL_H)
670 /* unlike an fd_set, poll takes any descriptor number */
671 struct pollfd pollset = WINPR_C_ARRAY_INIT;
672 pollset.fd = WINPR_ASSERTING_INT_CAST(int, sockfd);
673 pollset.events = POLLIN;
674 const UINT32 ms = (timeout > INT32_MAX) ? INT32_MAX : timeout;
675 (void)poll(&pollset, 1, WINPR_ASSERTING_INT_CAST(int, ms));
676#else
677#if !defined(_WIN32)
678 /* an fd_set only holds descriptors below FD_SETSIZE */
679 if (sockfd >= FD_SETSIZE)
680 {
681 Sleep(timeout);
682 return;
683 }
684#endif
685 fd_set rset;
686 FD_ZERO(&rset);
687 FD_SET(sockfd, &rset);
688 struct timeval tv = WINPR_C_ARRAY_INIT;
689 tv.tv_sec = WINPR_ASSERTING_INT_CAST(long, timeout / 1000);
690 tv.tv_usec = WINPR_ASSERTING_INT_CAST(long, (timeout % 1000) * 1000);
691#if defined(_WIN32)
692 (void)select(0, &rset, nullptr, nullptr, &tv); /* the first argument is ignored */
693#else
694 (void)select(WINPR_ASSERTING_INT_CAST(int, sockfd) + 1, &rset, nullptr, nullptr, &tv);
695#endif
696#endif
697}
698
699WINPR_ATTR_NODISCARD
700static BOOL emt_stopping(rdpEmt* emt)
701{
702 WINPR_ASSERT(emt);
703 return InterlockedCompareExchange(&emt->stop, 0, 0) != 0;
704}
705
706WINPR_ATTR_NODISCARD
707static DWORD WINAPI emt_thread(LPVOID arg)
708{
709 rdpEmt* emt = arg;
710 WINPR_ASSERT(emt);
711
712 while (!emt_stopping(emt))
713 {
714 EnterCriticalSection(&emt->lock);
715 const UINT32 timeout = rdpudp_get_timeout(emt->udp);
716 const SOCKET sockfd = rdpudp_get_socket(emt->udp);
717 LeaveCriticalSection(&emt->lock);
718
719 emt_wait_readable(sockfd, timeout);
720
721 if (emt_stopping(emt))
722 break;
723
724 EnterCriticalSection(&emt->lock);
725 const BOOL rc = emt_poll(emt);
726 LeaveCriticalSection(&emt->lock);
727 if (!rc)
728 break;
729 }
730
731 ExitThread(0);
732 return 0;
733}
734
735rdpEmt* rdpemt_new(wLog* log, UINT32 requestId, const BYTE* cookie, const char* hostname,
736 const BYTE* publicKey, size_t publicKeyLength, rdpEmtEventFn callback,
737 void* custom)
738{
739 WINPR_ASSERT(log);
740 WINPR_ASSERT(cookie);
741 WINPR_ASSERT(publicKey);
742 WINPR_ASSERT(callback);
743
744 rdpEmt* emt = calloc(1, sizeof(rdpEmt));
745 if (!emt)
746 return nullptr;
747
748 emt->log = log;
749 emt->requestId = requestId;
750 memcpy(emt->cookie, cookie, sizeof(emt->cookie));
751 emt->callback = callback;
752 emt->custom = custom;
753 if (!InitializeCriticalSectionAndSpinCount(&emt->lock, 4000))
754 {
755 free(emt);
756 return nullptr;
757 }
758
759 if (hostname)
760 {
761 emt->hostname = _strdup(hostname);
762 if (!emt->hostname)
763 goto fail;
764 }
765
766 emt->publicKey = malloc(publicKeyLength);
767 if (!emt->publicKey)
768 goto fail;
769 memcpy(emt->publicKey, publicKey, publicKeyLength);
770 emt->publicKeyLength = publicKeyLength;
771
772 /* [MS-RDPEUDP] 2.2.2.9: cookieHash is the SHA-256 hash of the securityCookie. It is sent
773 * with each 32 bit word byte swapped, which is what Windows accepts. */
774 BYTE digest[32] = WINPR_C_ARRAY_INIT;
775 if (!winpr_Digest(WINPR_MD_SHA256, emt->cookie, sizeof(emt->cookie), digest, sizeof(digest)))
776 goto fail;
777 BYTE cookieHash[32] = WINPR_C_ARRAY_INIT;
778 {
779 wStream dbuffer = WINPR_C_ARRAY_INIT;
780 wStream hbuffer = WINPR_C_ARRAY_INIT;
781 wStream* ds = Stream_StaticConstInit(&dbuffer, digest, sizeof(digest));
782 wStream* hs = Stream_StaticInit(&hbuffer, cookieHash, sizeof(cookieHash));
783 for (size_t x = 0; x < sizeof(cookieHash) / 4; x++)
784 {
785 /* the Stream_Write macros evaluate their value more than once */
786 const UINT32 word = Stream_Get_UINT32_BE(ds);
787 Stream_Write_UINT32(hs, word);
788 }
789 }
790
791 emt->in = Stream_New(nullptr, 16384);
792 emt->out = Stream_New(nullptr, 4096);
793 if (!emt->in || !emt->out)
794 goto fail;
795
796 emt->udp = rdpudp_new(log, cookieHash, emt_receive, emt);
797 if (!emt->udp)
798 goto fail;
799
800 return emt;
801fail:
802 WINPR_PRAGMA_DIAG_PUSH
803 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
804 rdpemt_free(emt);
805 WINPR_PRAGMA_DIAG_POP
806 return nullptr;
807}
808
809BOOL rdpemt_start(rdpEmt* emt, const struct sockaddr* addr, size_t addrlen)
810{
811 WINPR_ASSERT(emt);
812 WINPR_ASSERT(addr);
813
814 emt->started = GetTickCount64();
815 if (!rdpudp_connect(emt->udp, addr, addrlen))
816 return FALSE;
817
818 emt->thread = CreateThread(nullptr, 0, emt_thread, emt, 0, nullptr);
819 return emt->thread != nullptr;
820}
821
822void rdpemt_free(rdpEmt* emt)
823{
824 if (!emt)
825 return;
826
827 /* not InterlockedExchange: WinPR's reads the old value non-atomically */
828 InterlockedIncrement(&emt->stop);
829 if (emt->thread)
830 {
831 (void)WaitForSingleObject(emt->thread, INFINITE);
832 (void)CloseHandle(emt->thread);
833 }
834
835 SSL_free(emt->ssl); /* frees both BIOs */
836 SSL_CTX_free(emt->ctx);
837 rdpudp_free(emt->udp);
838 free(emt->hostname);
839 free(emt->publicKey);
840 Stream_Free(emt->in, TRUE);
841 Stream_Free(emt->out, TRUE);
842 DeleteCriticalSection(&emt->lock);
843 free(emt);
844}
845
846BOOL rdpemt_send_data(rdpEmt* emt, const BYTE* data, size_t length)
847{
848 WINPR_ASSERT(emt);
849 WINPR_ASSERT(data || (length == 0));
850
851 BOOL rc = FALSE;
852 EnterCriticalSection(&emt->lock);
853 if (emt->ready && !emt->failed)
854 {
855 rc = emt_write_pdu(emt, RDPTUNNEL_ACTION_DATA, nullptr, 0, data, length);
856 if (!rc)
857 emt_fail(emt, "failed to send tunnel data");
858 }
859 LeaveCriticalSection(&emt->lock);
860 return rc;
861}
862
863BOOL rdpemt_set_receiver(rdpEmt* emt, rdpEmtReceiverFn receiver, void* custom, wQueue* pending)
864{
865 WINPR_ASSERT(emt);
866 WINPR_ASSERT(receiver);
867 WINPR_ASSERT(pending);
868
869 /* The tunnel thread raises its events while it holds the lock, so pending does not fill up
870 * meanwhile. Once a receiver is set no data goes to pending any more, so whatever waits there
871 * then may be overtaken. */
872 EnterCriticalSection(&emt->lock);
873 const BOOL rc = emt->receiver || (Queue_Count(pending) == 0);
874 if (rc)
875 {
876 if (!emt->receiver)
877 WLog_Print(emt->log, WLOG_DEBUG,
878 "tunnel data goes to its receiver on the tunnel thread");
879 emt->receiver = receiver;
880 emt->receiverCustom = custom;
881 }
882 LeaveCriticalSection(&emt->lock);
883 return rc;
884}
885
886void rdpemt_clear_receiver(rdpEmt* emt)
887{
888 WINPR_ASSERT(emt);
889
890 /* the tunnel thread hands data up while it holds the lock, so it is out of the receiver now */
891 EnterCriticalSection(&emt->lock);
892 if (emt->receiver)
893 WLog_Print(emt->log, WLOG_DEBUG, "tunnel data goes to the event callback again");
894 emt->receiver = nullptr;
895 emt->receiverCustom = nullptr;
896 LeaveCriticalSection(&emt->lock);
897}
898
899UINT32 rdpemt_take_direct_bytes(rdpEmt* emt)
900{
901 WINPR_ASSERT(emt);
902
903 /* Not the lock: the main loop calls this on every pass. Swapped for 0 with compare-exchange,
904 * WinPR's InterlockedExchange reads the old value non-atomically. */
905 LONG bytes = InterlockedCompareExchange(&emt->directBytes, 0, 0);
906 while (bytes != 0)
907 {
908 const LONG seen = InterlockedCompareExchange(&emt->directBytes, 0, bytes);
909 if (seen == bytes)
910 break;
911 bytes = seen;
912 }
913 /* The counter wraps when the main loop leaves more than 2 GiB untaken. The count stays right
914 * modulo 2^32, like the UINT32 of a bandwidth measurement. */
915 return WINPR_CXX_COMPAT_CAST(UINT32, bytes);
916}
917
918BOOL rdpemt_send_subheader(rdpEmt* emt, const BYTE* subHeader, size_t length)
919{
920 WINPR_ASSERT(emt);
921 WINPR_ASSERT(subHeader);
922
923 BOOL rc = FALSE;
924 EnterCriticalSection(&emt->lock);
925 if (emt->ready && !emt->failed)
926 {
927 /* a Tunnel Data PDU without HigherLayerData, as Windows sends its auto-detect requests */
928 rc = emt_write_pdu(emt, RDPTUNNEL_ACTION_DATA, subHeader, length, nullptr, 0);
929 if (!rc)
930 emt_fail(emt, "failed to send a tunnel subheader");
931 }
932 LeaveCriticalSection(&emt->lock);
933 return rc;
934}
935
936BOOL rdpemt_is_supported(void)
937{
938 return TRUE;
939}
940
941UINT32 rdpemt_get_request_id(const rdpEmt* emt)
942{
943 WINPR_ASSERT(emt);
944 return emt->requestId;
945}