22 #include <freerdp/config.h>
23 #include <freerdp/version.h>
25 #include <winpr/assert.h>
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>
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>
42 #include "websocket.h"
44 #include "../credssp_auth.h"
47 #include "../../crypto/opensslcompat.h"
48 #include "rpc_fault.h"
51 #define TAG FREERDP_TAG("core.gateway.wst")
53 #define AUTH_PKG NEGO_SSP_NAME
71 static const char arm_query_param[] =
"%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=FreeRDP%%2F3.0";
73 static 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");
96 static 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);
135 static 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);
164 static BOOL wst_recv_auth_token(rdpCredsspAuth* auth, HttpResponse* response)
167 const char* token64 = NULL;
168 size_t authTokenLength = 0;
169 BYTE* authTokenData = NULL;
174 if (!auth || !response)
177 StatusCode = http_response_get_status_code(response);
180 case HTTP_STATUS_DENIED:
184 http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response);
188 token64 = http_response_get_auth_token(response, credssp_auth_pkg_name(auth));
193 len = strlen(token64);
195 crypto_base64_decode(token64, len, &authTokenData, &authTokenLength);
197 if (authTokenLength && (authTokenLength <= UINT32_MAX) && authTokenData)
199 authToken.pvBuffer = authTokenData;
200 authToken.cbBuffer = (UINT32)authTokenLength;
201 credssp_auth_take_input_buffer(auth, &authToken);
206 rc = credssp_auth_authenticate(auth);
213 static BOOL wst_tls_connect(rdpWst* wst, rdpTls* tls, UINT32 timeout)
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);
229 sockfd = freerdp_tcp_connect(wst->context, peerHostname, peerPort, timeout);
231 WLog_DBG(TAG,
"connecting to %s %d", peerHostname, peerPort);
237 socketBio = BIO_new(BIO_s_simple_socket());
241 closesocket((SOCKET)sockfd);
245 BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
246 bufferedBio = BIO_new(BIO_s_buffered_socket());
250 BIO_free_all(socketBio);
254 bufferedBio = BIO_push(bufferedBio, socketBio);
255 status = BIO_set_nonblock(bufferedBio, TRUE);
257 if (isProxyConnection)
259 if (!proxy_connect(wst->context, bufferedBio, proxyUsername, proxyPassword, wst->gwhostname,
262 BIO_free_all(bufferedBio);
269 BIO_free_all(bufferedBio);
273 tls->hostname = wst->gwhostname;
274 tls->port = MIN(UINT16_MAX, wst->gwport);
275 tls->isGatewayTransport = TRUE;
276 status = freerdp_tls_connect(tls, bufferedBio);
279 rdpContext* context = wst->context;
282 freerdp_set_last_error_if_not(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
286 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED);
291 return (status >= 1);
294 static wStream* wst_build_http_request(rdpWst* wst)
297 HttpRequest* request = NULL;
298 const char* uri = NULL;
303 uri = http_context_get_uri(wst->http);
304 request = http_request_new();
309 if (!http_request_set_method(request,
"GET") || !http_request_set_uri(request, uri))
312 if (wst->auth_required)
314 if (!wst_set_auth_header(wst->auth, request))
319 http_request_set_auth_scheme(request,
"Bearer");
320 http_request_set_auth_param(
325 s = http_request_write(wst->http, request);
327 http_request_free(request);
330 Stream_SealLength(s);
335 static BOOL wst_send_http_request(rdpWst* wst, rdpTls* tls)
340 wStream* s = wst_build_http_request(wst);
344 const size_t sz = Stream_Length(s);
345 int status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz);
347 Stream_Free(s, TRUE);
348 return (status >= 0);
351 static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, DWORD timeout,
355 WINPR_ASSERT(ppresponse);
356 WINPR_ASSERT(*ppresponse);
357 WINPR_ASSERT(pStatusCode);
360 const char* affinity = http_response_get_setcookie(*ppresponse,
"ARRAffinity");
363 WLog_DBG(TAG,
"Got Affinity cookie %s", affinity);
364 http_context_set_cookie(wst->http,
"ARRAffinity", affinity);
365 http_response_free(*ppresponse);
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);
373 wst->tls = freerdp_tls_new(wst->context);
374 if (!wst_tls_connect(wst, wst->tls, timeout))
380 char* urlWithAuth = NULL;
382 char firstParam = (strchr(wst->gwpath,
'?') != NULL) ?
'&' :
'?';
383 winpr_asprintf(&urlWithAuth, &urlLen, arm_query_param, wst->gwpath, firstParam,
385 FreeRDP_GatewayHttpExtAuthBearer));
389 wst->gwpath = urlWithAuth;
390 if (!http_context_set_uri(wst->http, wst->gwpath))
392 if (!http_context_enable_websocket_upgrade(wst->http, TRUE))
396 if (!wst_send_http_request(wst, wst->tls))
398 *ppresponse = http_response_recv(wst->tls, TRUE);
402 *pStatusCode = http_response_get_status_code(*ppresponse);
408 static BOOL wst_handle_denied(rdpWst* wst, HttpResponse** ppresponse,
long* pStatusCode)
411 WINPR_ASSERT(ppresponse);
412 WINPR_ASSERT(*ppresponse);
413 WINPR_ASSERT(pStatusCode);
418 if (!wst_auth_init(wst, wst->tls, AUTH_PKG))
420 if (!wst_send_http_request(wst, wst->tls))
423 http_response_free(*ppresponse);
424 *ppresponse = http_response_recv(wst->tls, TRUE);
428 while (!credssp_auth_is_complete(wst->auth))
430 if (!wst_recv_auth_token(wst->auth, *ppresponse))
433 if (credssp_auth_have_output_token(wst->auth))
435 if (!wst_send_http_request(wst, wst->tls))
438 http_response_free(*ppresponse);
439 *ppresponse = http_response_recv(wst->tls, TRUE);
444 *pStatusCode = http_response_get_status_code(*ppresponse);
448 BOOL wst_connect(rdpWst* wst, DWORD timeout)
450 HttpResponse* response = NULL;
454 if (!wst_tls_connect(wst, wst->tls, timeout))
463 http_context_enable_websocket_upgrade(wst->http, FALSE);
465 if (!wst_send_http_request(wst, wst->tls))
468 response = http_response_recv(wst->tls, TRUE);
474 StatusCode = http_response_get_status_code(response);
478 case HTTP_STATUS_FORBIDDEN:
480 success = wst_handle_ok_or_forbidden(wst, &response, timeout, &StatusCode);
483 case HTTP_STATUS_DENIED:
484 success = wst_handle_denied(wst, &response, &StatusCode);
487 http_response_log_error_status(WLog_Get(TAG), WLOG_WARN, response);
491 const BOOL isWebsocket = http_response_is_websocket(wst->http, response);
492 http_response_free(response);
498 wst->wscontext.state = WebsocketStateOpcodeAndFin;
499 wst->wscontext.responseStreamBuffer = NULL;
504 char buffer[64] = { 0 };
505 WLog_ERR(TAG,
"Unexpected HTTP status: %s",
506 freerdp_http_status_string_format(StatusCode, buffer, ARRAYSIZE(buffer)));
511 DWORD wst_get_event_handles(rdpWst* wst, HANDLE* events, DWORD count)
514 WINPR_ASSERT(wst != NULL);
518 if (events && (nCount < count))
520 BIO_get_event(wst->tls->bio, &events[nCount]);
530 static int wst_bio_write(BIO* bio,
const char* buf,
int num)
536 rdpWst* wst = (rdpWst*)BIO_get_data(bio);
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);
545 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
548 else if (status < num)
550 BIO_set_flags(bio, BIO_FLAGS_WRITE);
551 WSASetLastError(WSAEWOULDBLOCK);
555 BIO_set_flags(bio, BIO_FLAGS_WRITE);
561 static int wst_bio_read(BIO* bio,
char* buf,
int size)
566 WINPR_ASSERT(size >= 0);
568 rdpWst* wst = (rdpWst*)BIO_get_data(bio);
573 status = websocket_read(wst->tls->bio, (BYTE*)buf, (
size_t)size, &wst->wscontext);
576 if (!BIO_should_retry(wst->tls->bio))
584 BIO_clear_retry_flags(bio);
587 else if (status == 0)
589 BIO_set_retry_read(bio);
590 WSASetLastError(WSAEWOULDBLOCK);
595 BIO_set_flags(bio, BIO_FLAGS_READ);
601 static int wst_bio_puts(BIO* bio,
const char* str)
609 static int wst_bio_gets(BIO* bio,
char* str,
int size)
617 static long wst_bio_ctrl(BIO* bio,
int cmd,
long arg1,
void* arg2)
622 rdpWst* wst = (rdpWst*)BIO_get_data(bio);
624 rdpTls* tls = wst->tls;
626 if (cmd == BIO_CTRL_FLUSH)
628 (void)BIO_flush(tls->bio);
631 else if (cmd == BIO_C_SET_NONBLOCK)
635 else if (cmd == BIO_C_READ_BLOCKED)
637 status = BIO_read_blocked(tls->bio);
639 else if (cmd == BIO_C_WRITE_BLOCKED)
641 status = BIO_write_blocked(tls->bio);
643 else if (cmd == BIO_C_WAIT_READ)
645 int timeout = (int)arg1;
647 if (BIO_read_blocked(tls->bio))
648 return BIO_wait_read(tls->bio, timeout);
651 else if (cmd == BIO_C_WAIT_WRITE)
653 int timeout = (int)arg1;
655 if (BIO_write_blocked(tls->bio))
656 status = BIO_wait_write(tls->bio, timeout);
660 else if (cmd == BIO_C_GET_EVENT || cmd == BIO_C_GET_FD)
662 status = BIO_ctrl(tls->bio, cmd, arg1, arg2);
664 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
665 else if (cmd == BIO_CTRL_GET_KTLS_SEND)
673 else if (cmd == BIO_CTRL_GET_KTLS_RECV)
684 static int wst_bio_new(BIO* bio)
686 BIO_set_init(bio, 1);
687 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
691 static int wst_bio_free(BIO* bio)
697 static BIO_METHOD* BIO_s_wst(
void)
699 static BIO_METHOD* bio_methods = NULL;
701 if (bio_methods == NULL)
703 if (!(bio_methods = BIO_meth_new(BIO_TYPE_TSG,
"WSTransport")))
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);
718 static BOOL wst_parse_url(rdpWst* wst,
const char* url)
720 const char* hostStart = NULL;
721 const char* pos = NULL;
725 free(wst->gwhostname);
726 wst->gwhostname = NULL;
730 if (strncmp(
"wss://", url, 6) != 0)
732 if (strncmp(
"https://", url, 8) != 0)
734 WLog_ERR(TAG,
"Websocket URL is invalid. Only wss:// or https:// URLs are supported");
744 while (*pos !=
'\0' && *pos !=
':' && *pos !=
'/')
746 free(wst->gwhostname);
747 wst->gwhostname = NULL;
748 if (pos - hostStart == 0)
750 wst->gwhostname = malloc(
sizeof(
char) * (pos - hostStart + 1));
751 if (!wst->gwhostname)
753 strncpy(wst->gwhostname, hostStart, (pos - hostStart));
754 wst->gwhostname[pos - hostStart] =
'\0';
759 char* portNumberEnd = NULL;
761 const char* portStart = pos;
762 while (*pos !=
'\0' && *pos !=
'/')
764 if (pos - portStart > 5 || pos - portStart == 0)
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;
776 wst->gwpath = _strdup(pos);
782 rdpWst* wst_new(rdpContext* context)
789 wst = (rdpWst*)calloc(1,
sizeof(rdpWst));
793 wst->context = context;
795 wst->gwhostname = NULL;
799 if (!wst_parse_url(wst, context->settings->GatewayUrl))
800 goto wst_alloc_error;
802 wst->tls = freerdp_tls_new(wst->context);
804 goto wst_alloc_error;
806 wst->http = http_context_new();
809 goto wst_alloc_error;
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))
821 goto wst_alloc_error;
824 wst->frontBio = BIO_new(BIO_s_wst());
827 goto wst_alloc_error;
829 BIO_set_data(wst->frontBio, wst);
830 InitializeCriticalSection(&wst->writeSection);
831 wst->auth = credssp_auth_new(context);
833 goto wst_alloc_error;
838 WINPR_PRAGMA_DIAG_PUSH
839 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
841 WINPR_PRAGMA_DIAG_POP
845 void wst_free(rdpWst* wst)
850 freerdp_tls_free(wst->tls);
851 http_context_free(wst->http);
852 credssp_auth_free(wst->auth);
853 free(wst->gwhostname);
857 BIO_free_all(wst->frontBio);
859 DeleteCriticalSection(&wst->writeSection);
861 if (wst->wscontext.responseStreamBuffer != NULL)
862 Stream_Free(wst->wscontext.responseStreamBuffer, TRUE);
867 BIO* wst_get_front_bio_and_take_ownership(rdpWst* wst)
872 wst->attached = TRUE;
873 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.