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  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 
507 DWORD 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 
526 static 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 
558 static 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 
598 static 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)
606 static 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 
614 static 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 
681 static 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 
688 static int wst_bio_free(BIO* bio)
689 {
690  WINPR_UNUSED(bio);
691  return 1;
692 }
693 
694 static 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 
715 static 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 
777 rdpWst* 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;
833 wst_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 
841 void 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 
862 BIO* 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.