22#include <freerdp/config.h> 
   23#include <freerdp/version.h> 
   25#include <winpr/assert.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> 
   34#include "../settings.h" 
   36#include <freerdp/log.h> 
   37#include <freerdp/error.h> 
   38#include <freerdp/utils/ringbuffer.h> 
   39#include <freerdp/utils/smartcardlogon.h> 
   44#include "../credssp_auth.h" 
   47#include "../../crypto/opensslcompat.h" 
   51#define TAG FREERDP_TAG("core.gateway.wst") 
   53#define AUTH_PKG NEGO_SSP_NAME 
   68  websocket_context* wscontext;
 
   71static const char arm_query_param[] = 
"%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=%s";
 
   73static BOOL wst_get_gateway_credentials(rdpContext* context, rdp_auth_reason reason)
 
   75  WINPR_ASSERT(context);
 
   76  freerdp* instance = context->instance;
 
   78  auth_status rc = utils_authenticate_gateway(instance, reason);
 
   85      freerdp_set_last_error_log(instance->context, FREERDP_ERROR_CONNECT_CANCELLED);
 
   87    case AUTH_NO_CREDENTIALS:
 
   88      WLog_INFO(TAG, 
"No credentials provided - using NULL identity");
 
   96static BOOL wst_auth_init(rdpWst* wst, rdpTls* tls, TCHAR* authPkg)
 
  100  WINPR_ASSERT(authPkg);
 
  102  rdpContext* context = wst->context;
 
  103  rdpSettings* settings = context->settings;
 
  104  SEC_WINNT_AUTH_IDENTITY identity = { 0 };
 
  107  wst->auth_required = TRUE;
 
  108  if (!credssp_auth_init(wst->auth, authPkg, tls->Bindings))
 
  111  if (!wst_get_gateway_credentials(context, GW_AUTH_RDG))
 
  114  if (!identity_set_from_settings(&identity, settings, FreeRDP_GatewayUsername,
 
  115                                  FreeRDP_GatewayDomain, FreeRDP_GatewayPassword))
 
  118  SEC_WINNT_AUTH_IDENTITY* identityArg = (settings->GatewayUsername ? &identity : NULL);
 
  119  if (!credssp_auth_setup_client(wst->auth, 
"HTTP", wst->gwhostname, identityArg, NULL))
 
  121    sspi_FreeAuthIdentity(&identity);
 
  124  sspi_FreeAuthIdentity(&identity);
 
  126  credssp_auth_set_flags(wst->auth, ISC_REQ_CONFIDENTIALITY | ISC_REQ_MUTUAL_AUTH);
 
  128  rc = credssp_auth_authenticate(wst->auth);
 
  135static BOOL wst_set_auth_header(rdpCredsspAuth* auth, HttpRequest* request)
 
  138  WINPR_ASSERT(request);
 
  140  const SecBuffer* authToken = credssp_auth_get_output_buffer(auth);
 
  141  char* base64AuthToken = NULL;
 
  145    if (authToken->cbBuffer > INT_MAX)
 
  148    base64AuthToken = crypto_base64_encode(authToken->pvBuffer, authToken->cbBuffer);
 
  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);
 
  164static BOOL wst_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response)
 
  167  size_t authTokenLength = 0;
 
  168  BYTE* authTokenData = NULL;
 
  172  if (!auth || !response)
 
  175  const UINT16 StatusCode = http_response_get_status_code(response);
 
  178    case HTTP_STATUS_DENIED:
 
  182      http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response);
 
  186  const char* token64 = http_response_get_auth_token(response, credssp_auth_pkg_name(auth));
 
  191  len = strlen(token64);
 
  193  crypto_base64_decode(token64, len, &authTokenData, &authTokenLength);
 
  195  if (authTokenLength && (authTokenLength <= UINT32_MAX) && authTokenData)
 
  197    authToken.pvBuffer = authTokenData;
 
  198    authToken.cbBuffer = (UINT32)authTokenLength;
 
  199    credssp_auth_take_input_buffer(auth, &authToken);
 
  204  rc = credssp_auth_authenticate(auth);
 
  211static BOOL wst_tls_connect(rdpWst* wst, rdpTls* tls, UINT32 timeout)
 
  217  BIO* socketBio = NULL;
 
  218  BIO* bufferedBio = NULL;
 
  219  rdpSettings* settings = wst->context->settings;
 
  220  const char* peerHostname = wst->gwhostname;
 
  221  UINT16 peerPort = wst->gwport;
 
  222  const char* proxyUsername = NULL;
 
  223  const char* proxyPassword = NULL;
 
  224  BOOL isProxyConnection =
 
  225      proxy_prepare(settings, &peerHostname, &peerPort, &proxyUsername, &proxyPassword);
 
  227  sockfd = freerdp_tcp_connect(wst->context, peerHostname, peerPort, timeout);
 
  229  WLog_DBG(TAG, 
"connecting to %s %d", peerHostname, peerPort);
 
  235  socketBio = BIO_new(BIO_s_simple_socket());
 
  239    closesocket((SOCKET)sockfd);
 
  243  BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
 
  244  bufferedBio = BIO_new(BIO_s_buffered_socket());
 
  248    BIO_free_all(socketBio);
 
  252  bufferedBio = BIO_push(bufferedBio, socketBio);
 
  253  status = BIO_set_nonblock(bufferedBio, TRUE);
 
  255  if (isProxyConnection)
 
  257    if (!proxy_connect(wst->context, bufferedBio, proxyUsername, proxyPassword, wst->gwhostname,
 
  260      BIO_free_all(bufferedBio);
 
  267    BIO_free_all(bufferedBio);
 
  271  tls->hostname = wst->gwhostname;
 
  272  tls->port = MIN(UINT16_MAX, wst->gwport);
 
  273  tls->isGatewayTransport = TRUE;
 
  274  status = freerdp_tls_connect(tls, bufferedBio);
 
  277    rdpContext* context = wst->context;
 
  280      freerdp_set_last_error_if_not(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
 
  284      freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED);
 
  289  return (status >= 1);
 
  292static wStream* wst_build_http_request(rdpWst* wst)
 
  295  HttpRequest* request = NULL;
 
  296  const char* uri = NULL;
 
  301  uri = http_context_get_uri(wst->http);
 
  302  request = http_request_new();
 
  307  if (!http_request_set_method(request, 
"GET") || !http_request_set_uri(request, uri))
 
  310  if (wst->auth_required)
 
  312    if (!wst_set_auth_header(wst->auth, request))
 
  317    http_request_set_auth_scheme(request, 
"Bearer");
 
  318    http_request_set_auth_param(
 
  323  s = http_request_write(wst->http, request);
 
  325  http_request_free(request);
 
  328    Stream_SealLength(s);
 
  333static BOOL wst_send_http_request(rdpWst* wst, rdpTls* tls)
 
  338  wStream* s = wst_build_http_request(wst);
 
  342  const size_t sz = Stream_Length(s);
 
  343  int status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz);
 
  345  Stream_Free(s, TRUE);
 
  346  return (status >= 0);
 
  349static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, DWORD timeout,
 
  353  WINPR_ASSERT(ppresponse);
 
  354  WINPR_ASSERT(*ppresponse);
 
  355  WINPR_ASSERT(pStatusCode);
 
  358  const char* affinity = http_response_get_setcookie(*ppresponse, 
"ARRAffinity");
 
  359  const char* samesite = http_response_get_setcookie(*ppresponse, 
"ARRAffinitySameSite");
 
  360  if ((affinity || samesite) &&
 
  363    WLog_INFO(TAG, 
"Got ARRAffinity cookie         %s", affinity);
 
  364    WLog_INFO(TAG, 
"Got ARRAffinitySameSite cookie %s", samesite);
 
  366      http_context_set_cookie(wst->http, 
"ARRAffinity", affinity);
 
  368      http_context_set_cookie(wst->http, 
"ARRAffinitySameSite", samesite);
 
  369    http_response_free(*ppresponse);
 
  372    const long fd = BIO_get_fd(wst->tls->bio, NULL);
 
  373    if ((fd >= 0) && (fd <= INT32_MAX))
 
  374      closesocket((SOCKET)fd);
 
  375    freerdp_tls_free(wst->tls);
 
  377    wst->tls = freerdp_tls_new(wst->context);
 
  378    if (!wst_tls_connect(wst, wst->tls, timeout))
 
  384      char* urlWithAuth = NULL;
 
  386      char firstParam = (strchr(wst->gwpath, 
'?') != NULL) ? 
'&' : 
'?';
 
  388                                                       FreeRDP_GatewayHttpExtAuthBearer);
 
  391      winpr_asprintf(&urlWithAuth, &urlLen, arm_query_param, wst->gwpath, firstParam, bearer,
 
  396      wst->gwpath = urlWithAuth;
 
  397      if (!http_context_set_uri(wst->http, wst->gwpath))
 
  399      if (!http_context_enable_websocket_upgrade(wst->http, TRUE))
 
  403    if (!wst_send_http_request(wst, wst->tls))
 
  405    *ppresponse = http_response_recv(wst->tls, TRUE);
 
  409    *pStatusCode = http_response_get_status_code(*ppresponse);
 
  415static BOOL wst_handle_denied(rdpWst* wst, HttpResponse** ppresponse, UINT16* pStatusCode)
 
  418  WINPR_ASSERT(ppresponse);
 
  419  WINPR_ASSERT(*ppresponse);
 
  420  WINPR_ASSERT(pStatusCode);
 
  425  if (!wst_auth_init(wst, wst->tls, AUTH_PKG))
 
  427  if (!wst_send_http_request(wst, wst->tls))
 
  430  http_response_free(*ppresponse);
 
  431  *ppresponse = http_response_recv(wst->tls, TRUE);
 
  435  while (!credssp_auth_is_complete(wst->auth))
 
  437    if (!wst_recv_auth_token(wst->auth, *ppresponse))
 
  440    if (credssp_auth_have_output_token(wst->auth))
 
  442      if (!wst_send_http_request(wst, wst->tls))
 
  445      http_response_free(*ppresponse);
 
  446      *ppresponse = http_response_recv(wst->tls, TRUE);
 
  451  *pStatusCode = http_response_get_status_code(*ppresponse);
 
  455static BOOL wst_handle_http_code(rdpWst* wst, UINT16 StatusCode)
 
  459    case HTTP_STATUS_PAYMENT_REQ:
 
  460    case HTTP_STATUS_FORBIDDEN:
 
  461    case HTTP_STATUS_DENIED:
 
  462      freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_ACCESS_DENIED);
 
  464    case HTTP_STATUS_MOVED:
 
  465    case HTTP_STATUS_USE_PROXY:
 
  466    case HTTP_STATUS_BAD_REQUEST:
 
  467    case HTTP_STATUS_NOT_FOUND:
 
  468    case HTTP_STATUS_GONE:
 
  469      freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
 
  471    case HTTP_STATUS_SERVER_ERROR:
 
  472    case HTTP_STATUS_NOT_SUPPORTED:
 
  473    case HTTP_STATUS_BAD_GATEWAY:
 
  474    case HTTP_STATUS_SERVICE_UNAVAIL:
 
  475    case HTTP_STATUS_VERSION_NOT_SUP:
 
  476      freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
 
  478    case HTTP_STATUS_GATEWAY_TIMEOUT:
 
  479      freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_ACTIVATION_TIMEOUT);
 
  485  char buffer[64] = { 0 };
 
  486  WLog_ERR(TAG, 
"Unexpected HTTP status: %s",
 
  487           freerdp_http_status_string_format(StatusCode, buffer, ARRAYSIZE(buffer)));
 
  488  freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_FAILED);
 
  492BOOL wst_connect(rdpWst* wst, DWORD timeout)
 
  495  WINPR_ASSERT(wst->context);
 
  497  if (!wst_tls_connect(wst, wst->tls, timeout))
 
  499    freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_FAILED);
 
  510    http_context_enable_websocket_upgrade(wst->http, FALSE);
 
  512  if (!wst_send_http_request(wst, wst->tls))
 
  514    freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_FAILED);
 
  518  HttpResponse* response = http_response_recv(wst->tls, TRUE);
 
  521    freerdp_set_last_error_if_not(wst->context, FREERDP_ERROR_CONNECT_FAILED);
 
  525  UINT16 StatusCode = http_response_get_status_code(response);
 
  529    case HTTP_STATUS_FORBIDDEN:
 
  531      success = wst_handle_ok_or_forbidden(wst, &response, timeout, &StatusCode);
 
  534    case HTTP_STATUS_DENIED:
 
  535      success = wst_handle_denied(wst, &response, &StatusCode);
 
  538      http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response);
 
  542  const BOOL isWebsocket = http_response_is_websocket(wst->http, response);
 
  543  http_response_free(response);
 
  545    return wst_handle_http_code(wst, StatusCode);
 
  548    return websocket_context_reset(wst->wscontext);
 
  550  return wst_handle_http_code(wst, StatusCode);
 
  553DWORD wst_get_event_handles(rdpWst* wst, HANDLE* events, DWORD count)
 
  556  WINPR_ASSERT(wst != NULL);
 
  560    if (events && (nCount < count))
 
  562      BIO_get_event(wst->tls->bio, &events[nCount]);
 
  572static int wst_bio_write(BIO* bio, 
const char* buf, 
int num)
 
  578  rdpWst* wst = (rdpWst*)BIO_get_data(bio);
 
  580  BIO_clear_flags(bio, BIO_FLAGS_WRITE);
 
  581  EnterCriticalSection(&wst->writeSection);
 
  582  status = websocket_context_write(wst->wscontext, wst->tls->bio, (
const BYTE*)buf, num,
 
  583                                   WebsocketBinaryOpcode);
 
  584  LeaveCriticalSection(&wst->writeSection);
 
  588    BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
 
  591  else if (status < num)
 
  593    BIO_set_flags(bio, BIO_FLAGS_WRITE);
 
  594    WSASetLastError(WSAEWOULDBLOCK);
 
  598    BIO_set_flags(bio, BIO_FLAGS_WRITE);
 
  604static int wst_bio_read(BIO* bio, 
char* buf, 
int size)
 
  609  WINPR_ASSERT(size >= 0);
 
  611  rdpWst* wst = (rdpWst*)BIO_get_data(bio);
 
  616    status = websocket_context_read(wst->wscontext, wst->tls->bio, (BYTE*)buf, (
size_t)size);
 
  619      if (!BIO_should_retry(wst->tls->bio))
 
  627    BIO_clear_retry_flags(bio);
 
  630  else if (status == 0)
 
  632    BIO_set_retry_read(bio);
 
  633    WSASetLastError(WSAEWOULDBLOCK);
 
  638    BIO_set_flags(bio, BIO_FLAGS_READ);
 
  644static int wst_bio_puts(BIO* bio, 
const char* str)
 
  652static int wst_bio_gets(BIO* bio, 
char* str, 
int size)
 
  660static long wst_bio_ctrl(BIO* bio, 
int cmd, 
long arg1, 
void* arg2)
 
  665  rdpWst* wst = (rdpWst*)BIO_get_data(bio);
 
  667  rdpTls* tls = wst->tls;
 
  669  if (cmd == BIO_CTRL_FLUSH)
 
  671    (void)BIO_flush(tls->bio);
 
  674  else if (cmd == BIO_C_SET_NONBLOCK)
 
  678  else if (cmd == BIO_C_READ_BLOCKED)
 
  680    status = BIO_read_blocked(tls->bio);
 
  682  else if (cmd == BIO_C_WRITE_BLOCKED)
 
  684    status = BIO_write_blocked(tls->bio);
 
  686  else if (cmd == BIO_C_WAIT_READ)
 
  688    int timeout = (int)arg1;
 
  690    if (BIO_read_blocked(tls->bio))
 
  691      return BIO_wait_read(tls->bio, timeout);
 
  694  else if (cmd == BIO_C_WAIT_WRITE)
 
  696    int timeout = (int)arg1;
 
  698    if (BIO_write_blocked(tls->bio))
 
  699      status = BIO_wait_write(tls->bio, timeout);
 
  703  else if (cmd == BIO_C_GET_EVENT || cmd == BIO_C_GET_FD)
 
  705    status = BIO_ctrl(tls->bio, cmd, arg1, arg2);
 
  707#if OPENSSL_VERSION_NUMBER >= 0x30000000L 
  708  else if (cmd == BIO_CTRL_GET_KTLS_SEND)
 
  716  else if (cmd == BIO_CTRL_GET_KTLS_RECV)
 
  727static int wst_bio_new(BIO* bio)
 
  729  BIO_set_init(bio, 1);
 
  730  BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
 
  734static int wst_bio_free(BIO* bio)
 
  740static BIO_METHOD* BIO_s_wst(
void)
 
  742  static BIO_METHOD* bio_methods = NULL;
 
  744  if (bio_methods == NULL)
 
  746    if (!(bio_methods = BIO_meth_new(BIO_TYPE_TSG, 
"WSTransport")))
 
  749    BIO_meth_set_write(bio_methods, wst_bio_write);
 
  750    BIO_meth_set_read(bio_methods, wst_bio_read);
 
  751    BIO_meth_set_puts(bio_methods, wst_bio_puts);
 
  752    BIO_meth_set_gets(bio_methods, wst_bio_gets);
 
  753    BIO_meth_set_ctrl(bio_methods, wst_bio_ctrl);
 
  754    BIO_meth_set_create(bio_methods, wst_bio_new);
 
  755    BIO_meth_set_destroy(bio_methods, wst_bio_free);
 
  761static BOOL wst_parse_url(rdpWst* wst, 
const char* url)
 
  763  const char* hostStart = NULL;
 
  764  const char* pos = NULL;
 
  768  free(wst->gwhostname);
 
  769  wst->gwhostname = NULL;
 
  773  if (strncmp(
"wss://", url, 6) != 0)
 
  775    if (strncmp(
"https://", url, 8) != 0)
 
  777      WLog_ERR(TAG, 
"Websocket URL is invalid. Only wss:// or https:// URLs are supported");
 
  787  while (*pos != 
'\0' && *pos != 
':' && *pos != 
'/')
 
  789  free(wst->gwhostname);
 
  790  wst->gwhostname = NULL;
 
  791  if (pos - hostStart == 0)
 
  793  wst->gwhostname = strndup(hostStart, WINPR_ASSERTING_INT_CAST(
size_t, (pos - hostStart)));
 
  794  if (!wst->gwhostname)
 
  799    char port[6] = { 0 };
 
  800    char* portNumberEnd = NULL;
 
  802    const char* portStart = pos;
 
  803    while (*pos != 
'\0' && *pos != 
'/')
 
  805    if (pos - portStart > 5 || pos - portStart == 0)
 
  807    strncpy(port, portStart, WINPR_ASSERTING_INT_CAST(
size_t, (pos - portStart)));
 
  808    port[pos - portStart] = 
'\0';
 
  809    long _p = strtol(port, &portNumberEnd, 10);
 
  810    if (portNumberEnd && (*portNumberEnd == 
'\0') && (_p > 0) && (_p <= UINT16_MAX))
 
  811      wst->gwport = (uint16_t)_p;
 
  817  wst->gwpath = _strdup(pos);
 
  823rdpWst* wst_new(rdpContext* context)
 
  828  rdpWst* wst = (rdpWst*)calloc(1, 
sizeof(rdpWst));
 
  832  wst->context = context;
 
  834  wst->gwhostname = NULL;
 
  838  if (!wst_parse_url(wst, context->settings->GatewayUrl))
 
  839    goto wst_alloc_error;
 
  841  wst->tls = freerdp_tls_new(wst->context);
 
  843    goto wst_alloc_error;
 
  845  wst->http = http_context_new();
 
  848    goto wst_alloc_error;
 
  850  const char* useragent =
 
  852  const char* msuseragent =
 
  854  if (!http_context_set_uri(wst->http, wst->gwpath) ||
 
  855      !http_context_set_accept(wst->http, 
"*/*") ||
 
  856      !http_context_set_cache_control(wst->http, 
"no-cache") ||
 
  857      !http_context_set_pragma(wst->http, 
"no-cache") ||
 
  858      !http_context_set_connection(wst->http, 
"Keep-Alive") ||
 
  859      !http_context_set_user_agent(wst->http, useragent) ||
 
  860      !http_context_set_x_ms_user_agent(wst->http, msuseragent) ||
 
  861      !http_context_set_host(wst->http, wst->gwhostname) ||
 
  862      !http_context_enable_websocket_upgrade(wst->http, TRUE))
 
  864    goto wst_alloc_error;
 
  867  wst->frontBio = BIO_new(BIO_s_wst());
 
  870    goto wst_alloc_error;
 
  872  BIO_set_data(wst->frontBio, wst);
 
  873  InitializeCriticalSection(&wst->writeSection);
 
  874  wst->auth = credssp_auth_new(context);
 
  876    goto wst_alloc_error;
 
  878  wst->wscontext = websocket_context_new();
 
  880    goto wst_alloc_error;
 
  884  WINPR_PRAGMA_DIAG_PUSH
 
  885  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
 
  887  WINPR_PRAGMA_DIAG_POP
 
  891void wst_free(rdpWst* wst)
 
  896  freerdp_tls_free(wst->tls);
 
  897  http_context_free(wst->http);
 
  898  credssp_auth_free(wst->auth);
 
  899  free(wst->gwhostname);
 
  903    BIO_free_all(wst->frontBio);
 
  905  DeleteCriticalSection(&wst->writeSection);
 
  907  websocket_context_free(wst->wscontext);
 
  912BIO* wst_get_front_bio_and_take_ownership(rdpWst* wst)
 
  917  wst->attached = TRUE;
 
  918  return wst->frontBio;
 
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.