FreeRDP
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 
55 struct 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 
71 static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=FreeRDP%%2F3.0";
72 
73 static 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 
96 static 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 
135 static 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 
164 static 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 
213 static 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 
294 static 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);
326 out:
327  http_request_free(request);
328 
329  if (s)
330  Stream_SealLength(s);
331 
332  return s;
333 }
334 
335 static 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 
351 static 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 
408 static 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 
448 BOOL 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  {
498  wst->wscontext.state = WebsocketStateOpcodeAndFin;
499  wst->wscontext.responseStreamBuffer = NULL;
500  return TRUE;
501  }
502  else
503  {
504  char buffer[64] = { 0 };
505  WLog_ERR(TAG, "Unexpected HTTP status: %s",
506  freerdp_http_status_string_format(StatusCode, buffer, ARRAYSIZE(buffer)));
507  }
508  return FALSE;
509 }
510 
511 DWORD wst_get_event_handles(rdpWst* wst, HANDLE* events, DWORD count)
512 {
513  DWORD nCount = 0;
514  WINPR_ASSERT(wst != NULL);
515 
516  if (wst->tls)
517  {
518  if (events && (nCount < count))
519  {
520  BIO_get_event(wst->tls->bio, &events[nCount]);
521  nCount++;
522  }
523  else
524  return 0;
525  }
526 
527  return nCount;
528 }
529 
530 static int wst_bio_write(BIO* bio, const char* buf, int num)
531 {
532  int status = 0;
533  WINPR_ASSERT(bio);
534  WINPR_ASSERT(buf);
535 
536  rdpWst* wst = (rdpWst*)BIO_get_data(bio);
537  WINPR_ASSERT(wst);
538  BIO_clear_flags(bio, BIO_FLAGS_WRITE);
539  EnterCriticalSection(&wst->writeSection);
540  status = websocket_write(wst->tls->bio, (const BYTE*)buf, num, WebsocketBinaryOpcode);
541  LeaveCriticalSection(&wst->writeSection);
542 
543  if (status < 0)
544  {
545  BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
546  return -1;
547  }
548  else if (status < num)
549  {
550  BIO_set_flags(bio, BIO_FLAGS_WRITE);
551  WSASetLastError(WSAEWOULDBLOCK);
552  }
553  else
554  {
555  BIO_set_flags(bio, BIO_FLAGS_WRITE);
556  }
557 
558  return status;
559 }
560 
561 static int wst_bio_read(BIO* bio, char* buf, int size)
562 {
563  int status = 0;
564  WINPR_ASSERT(bio);
565  WINPR_ASSERT(buf);
566  WINPR_ASSERT(size >= 0);
567 
568  rdpWst* wst = (rdpWst*)BIO_get_data(bio);
569  WINPR_ASSERT(wst);
570 
571  while (status <= 0)
572  {
573  status = websocket_read(wst->tls->bio, (BYTE*)buf, (size_t)size, &wst->wscontext);
574  if (status <= 0)
575  {
576  if (!BIO_should_retry(wst->tls->bio))
577  return -1;
578  return 0;
579  }
580  }
581 
582  if (status < 0)
583  {
584  BIO_clear_retry_flags(bio);
585  return -1;
586  }
587  else if (status == 0)
588  {
589  BIO_set_retry_read(bio);
590  WSASetLastError(WSAEWOULDBLOCK);
591  return -1;
592  }
593  else
594  {
595  BIO_set_flags(bio, BIO_FLAGS_READ);
596  }
597 
598  return status;
599 }
600 
601 static int wst_bio_puts(BIO* bio, const char* str)
602 {
603  WINPR_UNUSED(bio);
604  WINPR_UNUSED(str);
605  return -2;
606 }
607 
608 // NOLINTNEXTLINE(readability-non-const-parameter)
609 static int wst_bio_gets(BIO* bio, char* str, int size)
610 {
611  WINPR_UNUSED(bio);
612  WINPR_UNUSED(str);
613  WINPR_UNUSED(size);
614  return -2;
615 }
616 
617 static long wst_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
618 {
619  long status = -1;
620  WINPR_ASSERT(bio);
621 
622  rdpWst* wst = (rdpWst*)BIO_get_data(bio);
623  WINPR_ASSERT(wst);
624  rdpTls* tls = wst->tls;
625 
626  if (cmd == BIO_CTRL_FLUSH)
627  {
628  (void)BIO_flush(tls->bio);
629  status = 1;
630  }
631  else if (cmd == BIO_C_SET_NONBLOCK)
632  {
633  status = 1;
634  }
635  else if (cmd == BIO_C_READ_BLOCKED)
636  {
637  status = BIO_read_blocked(tls->bio);
638  }
639  else if (cmd == BIO_C_WRITE_BLOCKED)
640  {
641  status = BIO_write_blocked(tls->bio);
642  }
643  else if (cmd == BIO_C_WAIT_READ)
644  {
645  int timeout = (int)arg1;
646 
647  if (BIO_read_blocked(tls->bio))
648  return BIO_wait_read(tls->bio, timeout);
649  status = 1;
650  }
651  else if (cmd == BIO_C_WAIT_WRITE)
652  {
653  int timeout = (int)arg1;
654 
655  if (BIO_write_blocked(tls->bio))
656  status = BIO_wait_write(tls->bio, timeout);
657  else
658  status = 1;
659  }
660  else if (cmd == BIO_C_GET_EVENT || cmd == BIO_C_GET_FD)
661  {
662  status = BIO_ctrl(tls->bio, cmd, arg1, arg2);
663  }
664 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
665  else if (cmd == BIO_CTRL_GET_KTLS_SEND)
666  {
667  /* Even though BIO_get_ktls_send says that returning negative values is valid
668  * openssl internal sources are full of if(!BIO_get_ktls_send && ) stuff. This has some
669  * nasty sideeffects. return 0 as proper no KTLS offloading flag
670  */
671  status = 0;
672  }
673  else if (cmd == BIO_CTRL_GET_KTLS_RECV)
674  {
675  /* Even though BIO_get_ktls_recv says that returning negative values is valid
676  * there is no reason to trust trust negative values are implemented right everywhere
677  */
678  status = 0;
679  }
680 #endif
681  return status;
682 }
683 
684 static int wst_bio_new(BIO* bio)
685 {
686  BIO_set_init(bio, 1);
687  BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
688  return 1;
689 }
690 
691 static int wst_bio_free(BIO* bio)
692 {
693  WINPR_UNUSED(bio);
694  return 1;
695 }
696 
697 static BIO_METHOD* BIO_s_wst(void)
698 {
699  static BIO_METHOD* bio_methods = NULL;
700 
701  if (bio_methods == NULL)
702  {
703  if (!(bio_methods = BIO_meth_new(BIO_TYPE_TSG, "WSTransport")))
704  return NULL;
705 
706  BIO_meth_set_write(bio_methods, wst_bio_write);
707  BIO_meth_set_read(bio_methods, wst_bio_read);
708  BIO_meth_set_puts(bio_methods, wst_bio_puts);
709  BIO_meth_set_gets(bio_methods, wst_bio_gets);
710  BIO_meth_set_ctrl(bio_methods, wst_bio_ctrl);
711  BIO_meth_set_create(bio_methods, wst_bio_new);
712  BIO_meth_set_destroy(bio_methods, wst_bio_free);
713  }
714 
715  return bio_methods;
716 }
717 
718 static BOOL wst_parse_url(rdpWst* wst, const char* url)
719 {
720  const char* hostStart = NULL;
721  const char* pos = NULL;
722  WINPR_ASSERT(wst);
723  WINPR_ASSERT(url);
724 
725  free(wst->gwhostname);
726  wst->gwhostname = NULL;
727  free(wst->gwpath);
728  wst->gwpath = NULL;
729 
730  if (strncmp("wss://", url, 6) != 0)
731  {
732  if (strncmp("https://", url, 8) != 0)
733  {
734  WLog_ERR(TAG, "Websocket URL is invalid. Only wss:// or https:// URLs are supported");
735  return FALSE;
736  }
737  else
738  hostStart = url + 8;
739  }
740  else
741  hostStart = url + 6;
742 
743  pos = hostStart;
744  while (*pos != '\0' && *pos != ':' && *pos != '/')
745  pos++;
746  free(wst->gwhostname);
747  wst->gwhostname = NULL;
748  if (pos - hostStart == 0)
749  return FALSE;
750  wst->gwhostname = malloc(sizeof(char) * (pos - hostStart + 1));
751  if (!wst->gwhostname)
752  return FALSE;
753  strncpy(wst->gwhostname, hostStart, (pos - hostStart));
754  wst->gwhostname[pos - hostStart] = '\0';
755 
756  if (*pos == ':')
757  {
758  char port[6];
759  char* portNumberEnd = NULL;
760  pos++;
761  const char* portStart = pos;
762  while (*pos != '\0' && *pos != '/')
763  pos++;
764  if (pos - portStart > 5 || pos - portStart == 0)
765  return FALSE;
766  strncpy(port, portStart, (pos - portStart));
767  port[pos - portStart] = '\0';
768  long _p = strtol(port, &portNumberEnd, 10);
769  if (portNumberEnd && (*portNumberEnd == '\0') && (_p > 0) && (_p <= UINT16_MAX))
770  wst->gwport = (uint16_t)_p;
771  else
772  return FALSE;
773  }
774  else
775  wst->gwport = 443;
776  wst->gwpath = _strdup(pos);
777  if (!wst->gwpath)
778  return FALSE;
779  return TRUE;
780 }
781 
782 rdpWst* wst_new(rdpContext* context)
783 {
784  rdpWst* wst = NULL;
785 
786  if (!context)
787  return NULL;
788 
789  wst = (rdpWst*)calloc(1, sizeof(rdpWst));
790 
791  if (wst)
792  {
793  wst->context = context;
794 
795  wst->gwhostname = NULL;
796  wst->gwport = 443;
797  wst->gwpath = NULL;
798 
799  if (!wst_parse_url(wst, context->settings->GatewayUrl))
800  goto wst_alloc_error;
801 
802  wst->tls = freerdp_tls_new(wst->context);
803  if (!wst->tls)
804  goto wst_alloc_error;
805 
806  wst->http = http_context_new();
807 
808  if (!wst->http)
809  goto wst_alloc_error;
810 
811  if (!http_context_set_uri(wst->http, wst->gwpath) ||
812  !http_context_set_accept(wst->http, "*/*") ||
813  !http_context_set_cache_control(wst->http, "no-cache") ||
814  !http_context_set_pragma(wst->http, "no-cache") ||
815  !http_context_set_connection(wst->http, "Keep-Alive") ||
816  !http_context_set_user_agent(wst->http, FREERDP_USER_AGENT) ||
817  !http_context_set_x_ms_user_agent(wst->http, FREERDP_USER_AGENT) ||
818  !http_context_set_host(wst->http, wst->gwhostname) ||
819  !http_context_enable_websocket_upgrade(wst->http, TRUE))
820  {
821  goto wst_alloc_error;
822  }
823 
824  wst->frontBio = BIO_new(BIO_s_wst());
825 
826  if (!wst->frontBio)
827  goto wst_alloc_error;
828 
829  BIO_set_data(wst->frontBio, wst);
830  InitializeCriticalSection(&wst->writeSection);
831  wst->auth = credssp_auth_new(context);
832  if (!wst->auth)
833  goto wst_alloc_error;
834  }
835 
836  return wst;
837 wst_alloc_error:
838  WINPR_PRAGMA_DIAG_PUSH
839  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
840  wst_free(wst);
841  WINPR_PRAGMA_DIAG_POP
842  return NULL;
843 }
844 
845 void wst_free(rdpWst* wst)
846 {
847  if (!wst)
848  return;
849 
850  freerdp_tls_free(wst->tls);
851  http_context_free(wst->http);
852  credssp_auth_free(wst->auth);
853  free(wst->gwhostname);
854  free(wst->gwpath);
855 
856  if (!wst->attached)
857  BIO_free_all(wst->frontBio);
858 
859  DeleteCriticalSection(&wst->writeSection);
860 
861  if (wst->wscontext.responseStreamBuffer != NULL)
862  Stream_Free(wst->wscontext.responseStreamBuffer, TRUE);
863 
864  free(wst);
865 }
866 
867 BIO* wst_get_front_bio_and_take_ownership(rdpWst* wst)
868 {
869  if (!wst)
870  return NULL;
871 
872  wst->attached = TRUE;
873  return wst->frontBio;
874 }
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.