FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
wst.c
1
20#include <stdint.h>
21
22#include <freerdp/config.h>
23#include <freerdp/version.h>
24
25#include <winpr/assert.h>
26
27#include <winpr/crt.h>
28#include <winpr/synch.h>
29#include <winpr/print.h>
30#include <winpr/stream.h>
31#include <winpr/winsock.h>
32#include <winpr/cred.h>
33
34#include "../settings.h"
35
36#include <freerdp/log.h>
37#include <freerdp/error.h>
38#include <freerdp/utils/ringbuffer.h>
39#include <freerdp/utils/smartcardlogon.h>
40
41#include "wst.h"
42#include "websocket.h"
43#include "http.h"
44#include "../credssp_auth.h"
45#include "../proxy.h"
46#include "../rdp.h"
47#include "../../crypto/opensslcompat.h"
48#include "rpc_fault.h"
49#include "../utils.h"
50
51#define TAG FREERDP_TAG("core.gateway.wst")
52
53#define AUTH_PKG NEGO_SSP_NAME
54
55struct rdp_wst
56{
57 rdpContext* context;
58 BOOL attached;
59 BIO* frontBio;
60 rdpTls* tls;
61 rdpCredsspAuth* auth;
62 BOOL auth_required;
63 HttpContext* http;
64 CRITICAL_SECTION writeSection;
65 char* gwhostname;
66 uint16_t gwport;
67 char* gwpath;
68 websocket_context* wscontext;
69};
70
71static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=FreeRDP%%2F3.0";
72
73static BOOL wst_get_gateway_credentials(rdpContext* context, rdp_auth_reason reason)
74{
75 WINPR_ASSERT(context);
76 freerdp* instance = context->instance;
77
78 auth_status rc = utils_authenticate_gateway(instance, reason);
79 switch (rc)
80 {
81 case AUTH_SUCCESS:
82 case AUTH_SKIP:
83 return TRUE;
84 case AUTH_CANCELLED:
85 freerdp_set_last_error_log(instance->context, FREERDP_ERROR_CONNECT_CANCELLED);
86 return FALSE;
87 case AUTH_NO_CREDENTIALS:
88 WLog_INFO(TAG, "No credentials provided - using NULL identity");
89 return TRUE;
90 case AUTH_FAILED:
91 default:
92 return FALSE;
93 }
94}
95
96static BOOL wst_auth_init(rdpWst* wst, rdpTls* tls, TCHAR* authPkg)
97{
98 WINPR_ASSERT(wst);
99 WINPR_ASSERT(tls);
100 WINPR_ASSERT(authPkg);
101
102 rdpContext* context = wst->context;
103 rdpSettings* settings = context->settings;
104 SEC_WINNT_AUTH_IDENTITY identity = { 0 };
105 int rc = 0;
106
107 wst->auth_required = TRUE;
108 if (!credssp_auth_init(wst->auth, authPkg, tls->Bindings))
109 return FALSE;
110
111 if (!wst_get_gateway_credentials(context, GW_AUTH_RDG))
112 return FALSE;
113
114 if (!identity_set_from_settings(&identity, settings, FreeRDP_GatewayUsername,
115 FreeRDP_GatewayDomain, FreeRDP_GatewayPassword))
116 return FALSE;
117
118 SEC_WINNT_AUTH_IDENTITY* identityArg = (settings->GatewayUsername ? &identity : NULL);
119 if (!credssp_auth_setup_client(wst->auth, "HTTP", wst->gwhostname, identityArg, NULL))
120 {
121 sspi_FreeAuthIdentity(&identity);
122 return FALSE;
123 }
124 sspi_FreeAuthIdentity(&identity);
125
126 credssp_auth_set_flags(wst->auth, ISC_REQ_CONFIDENTIALITY | ISC_REQ_MUTUAL_AUTH);
127
128 rc = credssp_auth_authenticate(wst->auth);
129 if (rc < 0)
130 return FALSE;
131
132 return TRUE;
133}
134
135static BOOL wst_set_auth_header(rdpCredsspAuth* auth, HttpRequest* request)
136{
137 WINPR_ASSERT(auth);
138 WINPR_ASSERT(request);
139
140 const SecBuffer* authToken = credssp_auth_get_output_buffer(auth);
141 char* base64AuthToken = NULL;
142
143 if (authToken)
144 {
145 if (authToken->cbBuffer > INT_MAX)
146 return FALSE;
147
148 base64AuthToken = crypto_base64_encode(authToken->pvBuffer, authToken->cbBuffer);
149 }
150
151 if (base64AuthToken)
152 {
153 BOOL rc = http_request_set_auth_scheme(request, credssp_auth_pkg_name(auth)) &&
154 http_request_set_auth_param(request, base64AuthToken);
155 free(base64AuthToken);
156
157 if (!rc)
158 return FALSE;
159 }
160
161 return TRUE;
162}
163
164static BOOL wst_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response)
165{
166 size_t len = 0;
167 const char* token64 = NULL;
168 size_t authTokenLength = 0;
169 BYTE* authTokenData = NULL;
170 SecBuffer authToken = { 0 };
171 long StatusCode = 0;
172 int rc = 0;
173
174 if (!auth || !response)
175 return FALSE;
176
177 StatusCode = http_response_get_status_code(response);
178 switch (StatusCode)
179 {
180 case HTTP_STATUS_DENIED:
181 case HTTP_STATUS_OK:
182 break;
183 default:
184 http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response);
185 return FALSE;
186 }
187
188 token64 = http_response_get_auth_token(response, credssp_auth_pkg_name(auth));
189
190 if (!token64)
191 return FALSE;
192
193 len = strlen(token64);
194
195 crypto_base64_decode(token64, len, &authTokenData, &authTokenLength);
196
197 if (authTokenLength && (authTokenLength <= UINT32_MAX) && authTokenData)
198 {
199 authToken.pvBuffer = authTokenData;
200 authToken.cbBuffer = (UINT32)authTokenLength;
201 credssp_auth_take_input_buffer(auth, &authToken);
202 }
203 else
204 free(authTokenData);
205
206 rc = credssp_auth_authenticate(auth);
207 if (rc < 0)
208 return FALSE;
209
210 return TRUE;
211}
212
213static BOOL wst_tls_connect(rdpWst* wst, rdpTls* tls, UINT32 timeout)
214{
215 WINPR_ASSERT(wst);
216 WINPR_ASSERT(tls);
217 int sockfd = 0;
218 long status = 0;
219 BIO* socketBio = NULL;
220 BIO* bufferedBio = NULL;
221 rdpSettings* settings = wst->context->settings;
222 const char* peerHostname = wst->gwhostname;
223 UINT16 peerPort = wst->gwport;
224 const char* proxyUsername = NULL;
225 const char* proxyPassword = NULL;
226 BOOL isProxyConnection =
227 proxy_prepare(settings, &peerHostname, &peerPort, &proxyUsername, &proxyPassword);
228
229 sockfd = freerdp_tcp_connect(wst->context, peerHostname, peerPort, timeout);
230
231 WLog_DBG(TAG, "connecting to %s %d", peerHostname, peerPort);
232 if (sockfd < 0)
233 {
234 return FALSE;
235 }
236
237 socketBio = BIO_new(BIO_s_simple_socket());
238
239 if (!socketBio)
240 {
241 closesocket((SOCKET)sockfd);
242 return FALSE;
243 }
244
245 BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
246 bufferedBio = BIO_new(BIO_s_buffered_socket());
247
248 if (!bufferedBio)
249 {
250 BIO_free_all(socketBio);
251 return FALSE;
252 }
253
254 bufferedBio = BIO_push(bufferedBio, socketBio);
255 status = BIO_set_nonblock(bufferedBio, TRUE);
256
257 if (isProxyConnection)
258 {
259 if (!proxy_connect(wst->context, bufferedBio, proxyUsername, proxyPassword, wst->gwhostname,
260 wst->gwport))
261 {
262 BIO_free_all(bufferedBio);
263 return FALSE;
264 }
265 }
266
267 if (!status)
268 {
269 BIO_free_all(bufferedBio);
270 return FALSE;
271 }
272
273 tls->hostname = wst->gwhostname;
274 tls->port = MIN(UINT16_MAX, wst->gwport);
275 tls->isGatewayTransport = TRUE;
276 status = freerdp_tls_connect(tls, bufferedBio);
277 if (status < 1)
278 {
279 rdpContext* context = wst->context;
280 if (status < 0)
281 {
282 freerdp_set_last_error_if_not(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
283 }
284 else
285 {
286 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED);
287 }
288
289 return FALSE;
290 }
291 return (status >= 1);
292}
293
294static wStream* wst_build_http_request(rdpWst* wst)
295{
296 wStream* s = NULL;
297 HttpRequest* request = NULL;
298 const char* uri = NULL;
299
300 if (!wst)
301 return NULL;
302
303 uri = http_context_get_uri(wst->http);
304 request = http_request_new();
305
306 if (!request)
307 return NULL;
308
309 if (!http_request_set_method(request, "GET") || !http_request_set_uri(request, uri))
310 goto out;
311
312 if (wst->auth_required)
313 {
314 if (!wst_set_auth_header(wst->auth, request))
315 goto out;
316 }
317 else if (freerdp_settings_get_string(wst->context->settings, FreeRDP_GatewayHttpExtAuthBearer))
318 {
319 http_request_set_auth_scheme(request, "Bearer");
320 http_request_set_auth_param(
321 request,
322 freerdp_settings_get_string(wst->context->settings, FreeRDP_GatewayHttpExtAuthBearer));
323 }
324
325 s = http_request_write(wst->http, request);
326out:
327 http_request_free(request);
328
329 if (s)
330 Stream_SealLength(s);
331
332 return s;
333}
334
335static BOOL wst_send_http_request(rdpWst* wst, rdpTls* tls)
336{
337 WINPR_ASSERT(wst);
338 WINPR_ASSERT(tls);
339
340 wStream* s = wst_build_http_request(wst);
341 if (!s)
342 return FALSE;
343
344 const size_t sz = Stream_Length(s);
345 int status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz);
346
347 Stream_Free(s, TRUE);
348 return (status >= 0);
349}
350
351static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, DWORD timeout,
352 long* pStatusCode)
353{
354 WINPR_ASSERT(wst);
355 WINPR_ASSERT(ppresponse);
356 WINPR_ASSERT(*ppresponse);
357 WINPR_ASSERT(pStatusCode);
358
359 /* AVD returns a 403 response with a ARRAffinity cookie set. retry with that cookie */
360 const char* affinity = http_response_get_setcookie(*ppresponse, "ARRAffinity");
361 if (affinity && freerdp_settings_get_bool(wst->context->settings, FreeRDP_GatewayArmTransport))
362 {
363 WLog_DBG(TAG, "Got Affinity cookie %s", affinity);
364 http_context_set_cookie(wst->http, "ARRAffinity", affinity);
365 http_response_free(*ppresponse);
366 *ppresponse = NULL;
367 /* Terminate this connection and make a new one with the Loadbalancing Cookie */
368 const long fd = BIO_get_fd(wst->tls->bio, NULL);
369 if ((fd >= 0) && (fd <= INT32_MAX))
370 closesocket((SOCKET)fd);
371 freerdp_tls_free(wst->tls);
372
373 wst->tls = freerdp_tls_new(wst->context);
374 if (!wst_tls_connect(wst, wst->tls, timeout))
375 return FALSE;
376
377 if (freerdp_settings_get_string(wst->context->settings, FreeRDP_GatewayHttpExtAuthBearer) &&
378 freerdp_settings_get_bool(wst->context->settings, FreeRDP_GatewayArmTransport))
379 {
380 char* urlWithAuth = NULL;
381 size_t urlLen = 0;
382 char firstParam = (strchr(wst->gwpath, '?') != NULL) ? '&' : '?';
383 winpr_asprintf(&urlWithAuth, &urlLen, arm_query_param, wst->gwpath, firstParam,
384 freerdp_settings_get_string(wst->context->settings,
385 FreeRDP_GatewayHttpExtAuthBearer));
386 if (!urlWithAuth)
387 return FALSE;
388 free(wst->gwpath);
389 wst->gwpath = urlWithAuth;
390 if (!http_context_set_uri(wst->http, wst->gwpath))
391 return FALSE;
392 if (!http_context_enable_websocket_upgrade(wst->http, TRUE))
393 return FALSE;
394 }
395
396 if (!wst_send_http_request(wst, wst->tls))
397 return FALSE;
398 *ppresponse = http_response_recv(wst->tls, TRUE);
399 if (!*ppresponse)
400 return FALSE;
401
402 *pStatusCode = http_response_get_status_code(*ppresponse);
403 }
404
405 return TRUE;
406}
407
408static BOOL wst_handle_denied(rdpWst* wst, HttpResponse** ppresponse, long* pStatusCode)
409{
410 WINPR_ASSERT(wst);
411 WINPR_ASSERT(ppresponse);
412 WINPR_ASSERT(*ppresponse);
413 WINPR_ASSERT(pStatusCode);
414
415 if (freerdp_settings_get_string(wst->context->settings, FreeRDP_GatewayHttpExtAuthBearer))
416 return FALSE;
417
418 if (!wst_auth_init(wst, wst->tls, AUTH_PKG))
419 return FALSE;
420 if (!wst_send_http_request(wst, wst->tls))
421 return FALSE;
422
423 http_response_free(*ppresponse);
424 *ppresponse = http_response_recv(wst->tls, TRUE);
425 if (!*ppresponse)
426 return FALSE;
427
428 while (!credssp_auth_is_complete(wst->auth))
429 {
430 if (!wst_recv_auth_token(wst->auth, *ppresponse))
431 return FALSE;
432
433 if (credssp_auth_have_output_token(wst->auth))
434 {
435 if (!wst_send_http_request(wst, wst->tls))
436 return FALSE;
437
438 http_response_free(*ppresponse);
439 *ppresponse = http_response_recv(wst->tls, TRUE);
440 if (!*ppresponse)
441 return FALSE;
442 }
443 }
444 *pStatusCode = http_response_get_status_code(*ppresponse);
445 return TRUE;
446}
447
448BOOL wst_connect(rdpWst* wst, DWORD timeout)
449{
450 HttpResponse* response = NULL;
451 long StatusCode = 0;
452
453 WINPR_ASSERT(wst);
454 if (!wst_tls_connect(wst, wst->tls, timeout))
455 return FALSE;
456 if (freerdp_settings_get_bool(wst->context->settings, FreeRDP_GatewayArmTransport))
457 {
458 /*
459 * If we are directed here from a ARM Gateway first
460 * we need to get a Loadbalancing Cookie (ARRAffinity)
461 * This is done by a plain GET request on the websocket URL
462 */
463 http_context_enable_websocket_upgrade(wst->http, FALSE);
464 }
465 if (!wst_send_http_request(wst, wst->tls))
466 return FALSE;
467
468 response = http_response_recv(wst->tls, TRUE);
469 if (!response)
470 {
471 return FALSE;
472 }
473
474 StatusCode = http_response_get_status_code(response);
475 BOOL success = TRUE;
476 switch (StatusCode)
477 {
478 case HTTP_STATUS_FORBIDDEN:
479 case HTTP_STATUS_OK:
480 success = wst_handle_ok_or_forbidden(wst, &response, timeout, &StatusCode);
481 break;
482
483 case HTTP_STATUS_DENIED:
484 success = wst_handle_denied(wst, &response, &StatusCode);
485 break;
486 default:
487 http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response);
488 break;
489 }
490
491 const BOOL isWebsocket = http_response_is_websocket(wst->http, response);
492 http_response_free(response);
493 if (!success)
494 return FALSE;
495
496 if (isWebsocket)
497 return websocket_context_reset(wst->wscontext);
498 else
499 {
500 char buffer[64] = { 0 };
501 WLog_ERR(TAG, "Unexpected HTTP status: %s",
502 freerdp_http_status_string_format(StatusCode, buffer, ARRAYSIZE(buffer)));
503 }
504 return FALSE;
505}
506
507DWORD wst_get_event_handles(rdpWst* wst, HANDLE* events, DWORD count)
508{
509 DWORD nCount = 0;
510 WINPR_ASSERT(wst != NULL);
511
512 if (wst->tls)
513 {
514 if (events && (nCount < count))
515 {
516 BIO_get_event(wst->tls->bio, &events[nCount]);
517 nCount++;
518 }
519 else
520 return 0;
521 }
522
523 return nCount;
524}
525
526static int wst_bio_write(BIO* bio, const char* buf, int num)
527{
528 int status = 0;
529 WINPR_ASSERT(bio);
530 WINPR_ASSERT(buf);
531
532 rdpWst* wst = (rdpWst*)BIO_get_data(bio);
533 WINPR_ASSERT(wst);
534 BIO_clear_flags(bio, BIO_FLAGS_WRITE);
535 EnterCriticalSection(&wst->writeSection);
536 status = websocket_context_write(wst->wscontext, wst->tls->bio, (const BYTE*)buf, num,
537 WebsocketBinaryOpcode);
538 LeaveCriticalSection(&wst->writeSection);
539
540 if (status < 0)
541 {
542 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
543 return -1;
544 }
545 else if (status < num)
546 {
547 BIO_set_flags(bio, BIO_FLAGS_WRITE);
548 WSASetLastError(WSAEWOULDBLOCK);
549 }
550 else
551 {
552 BIO_set_flags(bio, BIO_FLAGS_WRITE);
553 }
554
555 return status;
556}
557
558static int wst_bio_read(BIO* bio, char* buf, int size)
559{
560 int status = 0;
561 WINPR_ASSERT(bio);
562 WINPR_ASSERT(buf);
563 WINPR_ASSERT(size >= 0);
564
565 rdpWst* wst = (rdpWst*)BIO_get_data(bio);
566 WINPR_ASSERT(wst);
567
568 while (status <= 0)
569 {
570 status = websocket_context_read(wst->wscontext, wst->tls->bio, (BYTE*)buf, (size_t)size);
571 if (status <= 0)
572 {
573 if (!BIO_should_retry(wst->tls->bio))
574 return -1;
575 return 0;
576 }
577 }
578
579 if (status < 0)
580 {
581 BIO_clear_retry_flags(bio);
582 return -1;
583 }
584 else if (status == 0)
585 {
586 BIO_set_retry_read(bio);
587 WSASetLastError(WSAEWOULDBLOCK);
588 return -1;
589 }
590 else
591 {
592 BIO_set_flags(bio, BIO_FLAGS_READ);
593 }
594
595 return status;
596}
597
598static int wst_bio_puts(BIO* bio, const char* str)
599{
600 WINPR_UNUSED(bio);
601 WINPR_UNUSED(str);
602 return -2;
603}
604
605// NOLINTNEXTLINE(readability-non-const-parameter)
606static int wst_bio_gets(BIO* bio, char* str, int size)
607{
608 WINPR_UNUSED(bio);
609 WINPR_UNUSED(str);
610 WINPR_UNUSED(size);
611 return -2;
612}
613
614static long wst_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
615{
616 long status = -1;
617 WINPR_ASSERT(bio);
618
619 rdpWst* wst = (rdpWst*)BIO_get_data(bio);
620 WINPR_ASSERT(wst);
621 rdpTls* tls = wst->tls;
622
623 if (cmd == BIO_CTRL_FLUSH)
624 {
625 (void)BIO_flush(tls->bio);
626 status = 1;
627 }
628 else if (cmd == BIO_C_SET_NONBLOCK)
629 {
630 status = 1;
631 }
632 else if (cmd == BIO_C_READ_BLOCKED)
633 {
634 status = BIO_read_blocked(tls->bio);
635 }
636 else if (cmd == BIO_C_WRITE_BLOCKED)
637 {
638 status = BIO_write_blocked(tls->bio);
639 }
640 else if (cmd == BIO_C_WAIT_READ)
641 {
642 int timeout = (int)arg1;
643
644 if (BIO_read_blocked(tls->bio))
645 return BIO_wait_read(tls->bio, timeout);
646 status = 1;
647 }
648 else if (cmd == BIO_C_WAIT_WRITE)
649 {
650 int timeout = (int)arg1;
651
652 if (BIO_write_blocked(tls->bio))
653 status = BIO_wait_write(tls->bio, timeout);
654 else
655 status = 1;
656 }
657 else if (cmd == BIO_C_GET_EVENT || cmd == BIO_C_GET_FD)
658 {
659 status = BIO_ctrl(tls->bio, cmd, arg1, arg2);
660 }
661#if OPENSSL_VERSION_NUMBER >= 0x30000000L
662 else if (cmd == BIO_CTRL_GET_KTLS_SEND)
663 {
664 /* Even though BIO_get_ktls_send says that returning negative values is valid
665 * openssl internal sources are full of if(!BIO_get_ktls_send && ) stuff. This has some
666 * nasty sideeffects. return 0 as proper no KTLS offloading flag
667 */
668 status = 0;
669 }
670 else if (cmd == BIO_CTRL_GET_KTLS_RECV)
671 {
672 /* Even though BIO_get_ktls_recv says that returning negative values is valid
673 * there is no reason to trust trust negative values are implemented right everywhere
674 */
675 status = 0;
676 }
677#endif
678 return status;
679}
680
681static int wst_bio_new(BIO* bio)
682{
683 BIO_set_init(bio, 1);
684 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
685 return 1;
686}
687
688static int wst_bio_free(BIO* bio)
689{
690 WINPR_UNUSED(bio);
691 return 1;
692}
693
694static BIO_METHOD* BIO_s_wst(void)
695{
696 static BIO_METHOD* bio_methods = NULL;
697
698 if (bio_methods == NULL)
699 {
700 if (!(bio_methods = BIO_meth_new(BIO_TYPE_TSG, "WSTransport")))
701 return NULL;
702
703 BIO_meth_set_write(bio_methods, wst_bio_write);
704 BIO_meth_set_read(bio_methods, wst_bio_read);
705 BIO_meth_set_puts(bio_methods, wst_bio_puts);
706 BIO_meth_set_gets(bio_methods, wst_bio_gets);
707 BIO_meth_set_ctrl(bio_methods, wst_bio_ctrl);
708 BIO_meth_set_create(bio_methods, wst_bio_new);
709 BIO_meth_set_destroy(bio_methods, wst_bio_free);
710 }
711
712 return bio_methods;
713}
714
715static BOOL wst_parse_url(rdpWst* wst, const char* url)
716{
717 const char* hostStart = NULL;
718 const char* pos = NULL;
719 WINPR_ASSERT(wst);
720 WINPR_ASSERT(url);
721
722 free(wst->gwhostname);
723 wst->gwhostname = NULL;
724 free(wst->gwpath);
725 wst->gwpath = NULL;
726
727 if (strncmp("wss://", url, 6) != 0)
728 {
729 if (strncmp("https://", url, 8) != 0)
730 {
731 WLog_ERR(TAG, "Websocket URL is invalid. Only wss:// or https:// URLs are supported");
732 return FALSE;
733 }
734 else
735 hostStart = url + 8;
736 }
737 else
738 hostStart = url + 6;
739
740 pos = hostStart;
741 while (*pos != '\0' && *pos != ':' && *pos != '/')
742 pos++;
743 free(wst->gwhostname);
744 wst->gwhostname = NULL;
745 if (pos - hostStart == 0)
746 return FALSE;
747 wst->gwhostname = strndup(hostStart, WINPR_ASSERTING_INT_CAST(size_t, (pos - hostStart)));
748 if (!wst->gwhostname)
749 return FALSE;
750
751 if (*pos == ':')
752 {
753 char port[6] = { 0 };
754 char* portNumberEnd = NULL;
755 pos++;
756 const char* portStart = pos;
757 while (*pos != '\0' && *pos != '/')
758 pos++;
759 if (pos - portStart > 5 || pos - portStart == 0)
760 return FALSE;
761 strncpy(port, portStart, WINPR_ASSERTING_INT_CAST(size_t, (pos - portStart)));
762 port[pos - portStart] = '\0';
763 long _p = strtol(port, &portNumberEnd, 10);
764 if (portNumberEnd && (*portNumberEnd == '\0') && (_p > 0) && (_p <= UINT16_MAX))
765 wst->gwport = (uint16_t)_p;
766 else
767 return FALSE;
768 }
769 else
770 wst->gwport = 443;
771 wst->gwpath = _strdup(pos);
772 if (!wst->gwpath)
773 return FALSE;
774 return TRUE;
775}
776
777rdpWst* wst_new(rdpContext* context)
778{
779 if (!context)
780 return NULL;
781
782 rdpWst* wst = (rdpWst*)calloc(1, sizeof(rdpWst));
783 if (!wst)
784 return NULL;
785
786 wst->context = context;
787
788 wst->gwhostname = NULL;
789 wst->gwport = 443;
790 wst->gwpath = NULL;
791
792 if (!wst_parse_url(wst, context->settings->GatewayUrl))
793 goto wst_alloc_error;
794
795 wst->tls = freerdp_tls_new(wst->context);
796 if (!wst->tls)
797 goto wst_alloc_error;
798
799 wst->http = http_context_new();
800
801 if (!wst->http)
802 goto wst_alloc_error;
803
804 if (!http_context_set_uri(wst->http, wst->gwpath) ||
805 !http_context_set_accept(wst->http, "*/*") ||
806 !http_context_set_cache_control(wst->http, "no-cache") ||
807 !http_context_set_pragma(wst->http, "no-cache") ||
808 !http_context_set_connection(wst->http, "Keep-Alive") ||
809 !http_context_set_user_agent(wst->http, FREERDP_USER_AGENT) ||
810 !http_context_set_x_ms_user_agent(wst->http, FREERDP_USER_AGENT) ||
811 !http_context_set_host(wst->http, wst->gwhostname) ||
812 !http_context_enable_websocket_upgrade(wst->http, TRUE))
813 {
814 goto wst_alloc_error;
815 }
816
817 wst->frontBio = BIO_new(BIO_s_wst());
818
819 if (!wst->frontBio)
820 goto wst_alloc_error;
821
822 BIO_set_data(wst->frontBio, wst);
823 InitializeCriticalSection(&wst->writeSection);
824 wst->auth = credssp_auth_new(context);
825 if (!wst->auth)
826 goto wst_alloc_error;
827
828 wst->wscontext = websocket_context_new();
829 if (!wst->wscontext)
830 goto wst_alloc_error;
831
832 return wst;
833wst_alloc_error:
834 WINPR_PRAGMA_DIAG_PUSH
835 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
836 wst_free(wst);
837 WINPR_PRAGMA_DIAG_POP
838 return NULL;
839}
840
841void wst_free(rdpWst* wst)
842{
843 if (!wst)
844 return;
845
846 freerdp_tls_free(wst->tls);
847 http_context_free(wst->http);
848 credssp_auth_free(wst->auth);
849 free(wst->gwhostname);
850 free(wst->gwpath);
851
852 if (!wst->attached)
853 BIO_free_all(wst->frontBio);
854
855 DeleteCriticalSection(&wst->writeSection);
856
857 websocket_context_free(wst->wscontext);
858
859 free(wst);
860}
861
862BIO* wst_get_front_bio_and_take_ownership(rdpWst* wst)
863{
864 if (!wst)
865 return NULL;
866
867 wst->attached = TRUE;
868 return wst->frontBio;
869}
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.