Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/FreeRDP/FreeRDP.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorakallabeth <akallabeth@users.noreply.github.com>2021-10-21 09:30:50 +0300
committerGitHub <noreply@github.com>2021-10-21 09:30:50 +0300
commitd39a7ba5c38e3ba3b99b1558dc2ab0970cbfb0c5 (patch)
tree9da79ce9e71b23e3ec22f5bf7b75a1f4be2a42f6
parent7f05f1a62af0fe4275717c4df8c836c0b6c9b711 (diff)
Stable 2.0 backports (#7366)2.4.1
* Fixed typo in changelog * Fix FIPS mode support and build with OpenSSL 3.0 FreeRDP fails to build with OpenSSL 3.0 because of usage of the `FIPS_mode` and `FIPS_mode_set` functions, which were removed there. Just a note that the FIPS mode is not supported by OpenSSL 1.1.* although the mentioned functions are still there (see https://wiki.openssl.org/index.php/FIPS_modules). Let's make FreeRDP build with OpenSSL 3.0 and fix the FIPS mode support. See: https://bugzilla.redhat.com/show_bug.cgi?id=1952937 (cherry picked from commit 26bf2816c3e0daeaf524c47cf0fcda8ae13b65ad) * winpr: avoid calling FIPS_mode() with OpenSSL 3.0 Fixes: 26bf2816c3e0daeaf524c47cf0fcda8ae13b65ad (cherry picked from commit 0c81c73c8d770fd5ffbc541dc176da515b66686b) * winpr/crypto: Load legacy provider to fix rc4 with OpenSSL 3.0 (#7210) * winpr/crypto: Exit cleanly when EVP_EncryptInit_ex fails The `EVP_EncryptInit_ex` function may fail in certain configurations. Consequently, FreeRDP segfaults in `EVP_CIPHER_CTX_set_key_length`. Let's handle the `EVP_EncryptInit_ex` failures and exit cleanly in such case. * winpr/crypto: Load legacy provider to fix rc4 with OpenSSL 3.0 Currently, the `EVP_EncryptInit_ex` function fails for rc4 with OpenSSL 3.0. This is becuase rc4 is provided by the legacy provider which is not loaded by default. Let's explicitly load the legacy provider to make FreeRDP work with OpenSSL 3.0. Relates: https://github.com/openssl/openssl/issues/14392 Fixes: https://github.com/FreeRDP/FreeRDP/issues/6604 (cherry picked from commit 67f3fff2c8bef678809572248276e6008532da43) Conflicts: winpr/libwinpr/crypto/cipher.c * Fix TestUnicodeConversion on big endian machines (#7219) Wide character literals are stored in native byte order. Use an array of bytes as a reference instead. Fixes: https://github.com/FreeRDP/FreeRDP/issues/6968 (cherry picked from commit 5208a67ea77d8c69048756e32a8612bc1e064f88) * Fixed async-input quit * Fix data write on usb channel * Bitmap update fix (#7349) * Added checks for bitmap width and heigth values Data received from the server might have invalid values for bitmap with or height. Abort parsing if such a value is found. Reported by Sunglin from the Knownsec 404 team & 0103 sec team * Added checks for glyph width & height * Fixed #7363: Length checks in ConvertUTF8toUTF16 (cherry picked from commit 623a77258a1610b6e37616f4613d1eb793edf4aa) * Implemented missing TSG debug functions (cherry picked from commit c06c4638061b0305a269dbb8f7c87009cf886f02) * Refactored RPC gateway parser Utilize wStream instead of custom binary parsing code, add proper input validation. Reported by Sunglin from the Knownsec 404 team & 0103 sec team (cherry picked from commit f0b44da67c09488178000725ff9f2729ccfdf9fe) * Replaced chmod with safer fchmod * Use fchmod so the file may not change underneath * Add unit tests for SetFileAttributesA * Add warning logs for unsupported flags (cherry picked from commit c8571dd5fd42e6c462ef5db692bce6adb4b67c8d) * Updated changelog and version * Added more explicit warning for deprecated command line * Add exit codes for more errors (cherry picked from commit aa4c3dfb8a60a5b6c7a50d330e33ca1e0724bf3a) * Updated changelog. Co-authored-by: Ondrej Holy <oholy@redhat.com> Co-authored-by: Mike Gilbert <floppym@gentoo.org> Co-authored-by: Ondrej Holy <ondrejholy@users.noreply.github.com> Co-authored-by: Ian Weir <mr.weir@me.com>
-rw-r--r--CMakeLists.txt2
-rw-r--r--ChangeLog18
-rw-r--r--client/Android/android_freerdp.c2
-rw-r--r--client/X11/xf_client.c80
-rw-r--r--client/X11/xfreerdp.h27
-rw-r--r--client/common/cmdline.c3
-rw-r--r--libfreerdp/core/gateway/ncacn_http.c6
-rw-r--r--libfreerdp/core/gateway/rdg.c77
-rw-r--r--libfreerdp/core/gateway/rpc.c134
-rw-r--r--libfreerdp/core/gateway/rpc.h18
-rw-r--r--libfreerdp/core/gateway/rpc_bind.c253
-rw-r--r--libfreerdp/core/gateway/rpc_bind.h2
-rw-r--r--libfreerdp/core/gateway/rpc_client.c265
-rw-r--r--libfreerdp/core/gateway/rpc_client.h3
-rw-r--r--libfreerdp/core/gateway/rpc_fault.c48
-rw-r--r--libfreerdp/core/gateway/rts.c1997
-rw-r--r--libfreerdp/core/gateway/rts.h29
-rw-r--r--libfreerdp/core/gateway/rts_signature.c127
-rw-r--r--libfreerdp/core/gateway/rts_signature.h8
-rw-r--r--libfreerdp/core/gateway/tsg.c543
-rw-r--r--libfreerdp/core/orders.c14
-rw-r--r--libfreerdp/core/surface.c45
-rw-r--r--libfreerdp/core/update.c7
-rw-r--r--winpr/CMakeLists.txt2
-rw-r--r--winpr/libwinpr/crt/utf.c4
-rw-r--r--winpr/libwinpr/file/generic.c88
-rw-r--r--winpr/libwinpr/file/test/CMakeLists.txt106
-rw-r--r--winpr/libwinpr/file/test/TestSetFileAttributes.c149
28 files changed, 3076 insertions, 981 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 918d211f3..a0687025a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER})
endif()
set(WITH_LIBRARY_VERSIONING "ON")
-set(RAW_VERSION_STRING "2.4.0")
+set(RAW_VERSION_STRING "2.4.1")
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
elseif(USE_VERSION_FROM_GIT_TAG)
diff --git a/ChangeLog b/ChangeLog
index 1389c23b6..266238305 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+# 2021-10-20 Version 2.4.1
+
+Noteworthy changes:
+* Refactored RPC gateway parsing code
+* OpenSSL 3.0 compatibility fixes
+* USB redirection: fixed transfer lengths
+
+Fixed issues:
+* #7363: Length checks in ConvertUTF8toUTF16
+* #7349: Added checks for bitmap width and heigth values
+
+Important notes:
+* CVE-2021-41159: Improper client input validation for gateway connections allows to overwrite memory
+* CVE-2021-41160: Improper region checks in all clients allow out of bound write to memory
+
+For a complete and detailed change log since the last release run:
+git log 2.4.0..2.4.1
+
# 2021-07-27 Version 2.4.0
Noteworthy changes:
diff --git a/client/Android/android_freerdp.c b/client/Android/android_freerdp.c
index 4546f41c5..7a4686a5a 100644
--- a/client/Android/android_freerdp.c
+++ b/client/Android/android_freerdp.c
@@ -56,7 +56,7 @@
#define TAG CLIENT_TAG("android")
/* Defines the JNI version supported by this library. */
-#define FREERDP_JNI_VERSION "2.4.0"
+#define FREERDP_JNI_VERSION "2.4.1"
static void android_OnChannelConnectedEventHandler(void* context, ChannelConnectedEventArgs* e)
{
diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c
index 114dc6d88..78852a745 100644
--- a/client/X11/xf_client.c
+++ b/client/X11/xf_client.c
@@ -1512,6 +1512,84 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
exit_code = XF_EXIT_NEGO_FAILURE;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_LOGON_FAILURE)
+ exit_code = XF_EXIT_LOGON_FAILURE;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT)
+ exit_code = XF_EXIT_ACCOUNT_LOCKED_OUT;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_PRE_CONNECT_FAILED)
+ exit_code = XF_EXIT_PRE_CONNECT_FAILED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_UNDEFINED)
+ exit_code = XF_EXIT_CONNECT_UNDEFINED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_POST_CONNECT_FAILED)
+ exit_code = XF_EXIT_POST_CONNECT_FAILED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_DNS_ERROR)
+ exit_code = XF_EXIT_DNS_ERROR;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_DNS_NAME_NOT_FOUND)
+ exit_code = XF_EXIT_DNS_NAME_NOT_FOUND;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_FAILED)
+ exit_code = XF_EXIT_CONNECT_FAILED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR)
+ exit_code = XF_EXIT_MCS_CONNECT_INITIAL_ERROR;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_TLS_CONNECT_FAILED)
+ exit_code = XF_EXIT_TLS_CONNECT_FAILED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_INSUFFICIENT_PRIVILEGES)
+ exit_code = XF_EXIT_INSUFFICIENT_PRIVILEGES;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_CANCELLED)
+ exit_code = XF_EXIT_CONNECT_CANCELLED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
+ exit_code = XF_EXIT_SECURITY_NEGO_CONNECT_FAILED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_TRANSPORT_FAILED)
+ exit_code = XF_EXIT_CONNECT_TRANSPORT_FAILED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED)
+ exit_code = XF_EXIT_CONNECT_PASSWORD_EXPIRED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE)
+ exit_code = XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_KDC_UNREACHABLE)
+ exit_code = XF_EXIT_CONNECT_KDC_UNREACHABLE;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED)
+ exit_code = XF_EXIT_CONNECT_ACCOUNT_DISABLED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED)
+ exit_code = XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_CLIENT_REVOKED)
+ exit_code = XF_EXIT_CONNECT_CLIENT_REVOKED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_WRONG_PASSWORD)
+ exit_code = XF_EXIT_CONNECT_WRONG_PASSWORD;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_ACCESS_DENIED)
+ exit_code = XF_EXIT_CONNECT_ACCESS_DENIED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION)
+ exit_code = XF_EXIT_CONNECT_ACCOUNT_RESTRICTION;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED)
+ exit_code = XF_EXIT_CONNECT_ACCOUNT_EXPIRED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED)
+ exit_code = XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED;
+ else if (freerdp_get_last_error(instance->context) ==
+ FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS)
+ exit_code = XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS;
else
exit_code = XF_EXIT_CONN_FAILED;
}
@@ -1676,7 +1754,7 @@ end:
DWORD xf_exit_code_from_disconnect_reason(DWORD reason)
{
- if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_NEGO_FAILURE))
+ if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS))
return reason;
/* License error set */
else if (reason >= 0x100 && reason <= 0x10A)
diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h
index b4488489a..636e60ad1 100644
--- a/client/X11/xfreerdp.h
+++ b/client/X11/xfreerdp.h
@@ -318,7 +318,32 @@ enum XF_EXIT_CODE
XF_EXIT_CONN_FAILED = 131,
XF_EXIT_AUTH_FAILURE = 132,
XF_EXIT_NEGO_FAILURE = 133,
-
+ XF_EXIT_LOGON_FAILURE = 134,
+ XF_EXIT_ACCOUNT_LOCKED_OUT = 135,
+ XF_EXIT_PRE_CONNECT_FAILED = 136,
+ XF_EXIT_CONNECT_UNDEFINED = 137,
+ XF_EXIT_POST_CONNECT_FAILED = 138,
+ XF_EXIT_DNS_ERROR = 139,
+ XF_EXIT_DNS_NAME_NOT_FOUND = 140,
+ XF_EXIT_CONNECT_FAILED = 141,
+ XF_EXIT_MCS_CONNECT_INITIAL_ERROR = 142,
+ XF_EXIT_TLS_CONNECT_FAILED = 143,
+ XF_EXIT_INSUFFICIENT_PRIVILEGES = 144,
+ XF_EXIT_CONNECT_CANCELLED = 145,
+ XF_EXIT_SECURITY_NEGO_CONNECT_FAILED = 146,
+ XF_EXIT_CONNECT_TRANSPORT_FAILED = 147,
+ XF_EXIT_CONNECT_PASSWORD_EXPIRED = 148,
+ XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE = 149,
+ XF_EXIT_CONNECT_KDC_UNREACHABLE = 150,
+ XF_EXIT_CONNECT_ACCOUNT_DISABLED = 151,
+ XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED = 152,
+ XF_EXIT_CONNECT_CLIENT_REVOKED = 153,
+ XF_EXIT_CONNECT_WRONG_PASSWORD = 154,
+ XF_EXIT_CONNECT_ACCESS_DENIED = 155,
+ XF_EXIT_CONNECT_ACCOUNT_RESTRICTION = 156,
+ XF_EXIT_CONNECT_ACCOUNT_EXPIRED = 157,
+ XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED = 158,
+ XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS = 159,
XF_EXIT_UNKNOWN = 255,
};
diff --git a/client/common/cmdline.c b/client/common/cmdline.c
index 6982eefe3..e6b3e55d8 100644
--- a/client/common/cmdline.c
+++ b/client/common/cmdline.c
@@ -1606,7 +1606,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
#if !defined(DEFINE_NO_DEPRECATED)
if (compatibility)
{
+ WLog_WARN(TAG, "----------------------------------------");
WLog_WARN(TAG, "Using deprecated command-line interface!");
+ WLog_WARN(TAG, "This will be removed with FreeRDP 3!");
+ WLog_WARN(TAG, "----------------------------------------");
return freerdp_client_parse_old_command_line_arguments(argc, argv, settings);
}
else
diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c
index cffb378eb..f288a0f3c 100644
--- a/libfreerdp/core/gateway/ncacn_http.c
+++ b/libfreerdp/core/gateway/ncacn_http.c
@@ -105,7 +105,7 @@ BOOL rpc_ncacn_http_send_in_channel_request(RpcChannel* inChannel)
BOOL rpc_ncacn_http_recv_in_channel_response(RpcChannel* inChannel, HttpResponse* response)
{
const char* token64 = NULL;
- int ntlmTokenLength = 0;
+ size_t ntlmTokenLength = 0;
BYTE* ntlmTokenData = NULL;
rdpNtlm* ntlm;
@@ -121,6 +121,7 @@ BOOL rpc_ncacn_http_recv_in_channel_response(RpcChannel* inChannel, HttpResponse
if (ntlmTokenData && ntlmTokenLength)
return ntlm_client_set_input_buffer(ntlm, FALSE, ntlmTokenData, ntlmTokenLength);
+ free(ntlmTokenData);
return TRUE;
}
@@ -258,7 +259,7 @@ BOOL rpc_ncacn_http_send_out_channel_request(RpcChannel* outChannel, BOOL replac
BOOL rpc_ncacn_http_recv_out_channel_response(RpcChannel* outChannel, HttpResponse* response)
{
const char* token64 = NULL;
- int ntlmTokenLength = 0;
+ size_t ntlmTokenLength = 0;
BYTE* ntlmTokenData = NULL;
rdpNtlm* ntlm;
@@ -274,5 +275,6 @@ BOOL rpc_ncacn_http_recv_out_channel_response(RpcChannel* outChannel, HttpRespon
if (ntlmTokenData && ntlmTokenLength)
return ntlm_client_set_input_buffer(ntlm, FALSE, ntlmTokenData, ntlmTokenLength);
+ free(ntlmTokenData);
return TRUE;
}
diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c
index f9ed72501..72019ede8 100644
--- a/libfreerdp/core/gateway/rdg.c
+++ b/libfreerdp/core/gateway/rdg.c
@@ -139,6 +139,7 @@ typedef struct
WEBSOCKET_STATE state;
wStream* responseStreamBuffer;
} rdg_http_websocket_context;
+
typedef enum _CHUNK_STATE
{
ChunkStateLenghHeader,
@@ -358,12 +359,15 @@ static BOOL rdg_write_chunked(BIO* bio, wStream* sPacket)
len = Stream_Length(sChunk);
if (len > INT_MAX)
+ {
+ Stream_Free(sChunk, TRUE);
return FALSE;
+ }
status = BIO_write(bio, Stream_Buffer(sChunk), (int)len);
Stream_Free(sChunk, TRUE);
- if (status != len)
+ if (status != (SSIZE_T)len)
return FALSE;
return TRUE;
@@ -437,7 +441,7 @@ static BOOL rdg_write_websocket(BIO* bio, wStream* sPacket, WEBSOCKET_OPCODE opc
status = BIO_write(bio, Stream_Buffer(sWS), Stream_Length(sWS));
Stream_Free(sWS, TRUE);
- if (status != fullLen)
+ if (status != (SSIZE_T)fullLen)
return FALSE;
return TRUE;
@@ -559,11 +563,13 @@ static BOOL rdg_websocket_reply_close(BIO* bio, wStream* s)
{
uint16_t data;
Stream_Read_UINT16(s, data);
- Stream_Write_UINT16(s, data ^ maskingKey1);
+ Stream_Write_UINT16(closeFrame, data ^ maskingKey1);
}
Stream_SealLength(closeFrame);
status = BIO_write(bio, Stream_Buffer(closeFrame), Stream_Length(closeFrame));
+ Stream_Free(closeFrame, TRUE);
+
/* server MUST close socket now. The server is not allowed anymore to
* send frames but if he does, nothing bad would happen */
if (status < 0)
@@ -742,7 +748,6 @@ static int rdg_websocket_read(BIO* bio, BYTE* pBuffer, size_t size,
TAG, "Websocket Server sends data with masking key. This is against RFC 6455.");
return -1;
}
- break;
case WebSocketStatePayload:
{
status = rdg_websocket_handle_payload(bio, pBuffer, size, encodingContext);
@@ -761,6 +766,7 @@ static int rdg_websocket_read(BIO* bio, BYTE* pBuffer, size_t size,
/* should be unreachable */
return -1;
}
+
static int rdg_chuncked_read(BIO* bio, BYTE* pBuffer, size_t size,
rdg_http_encoding_chunked_context* encodingContext)
{
@@ -832,9 +838,7 @@ static int rdg_chuncked_read(BIO* bio, BYTE* pBuffer, size_t size,
else
return (effectiveDataLen > 0 ? effectiveDataLen : status);
}
-
*dst = '\0';
-
/* strtoul is tricky, error are reported via errno, we also need
* to ensure the result does not overflow */
errno = 0;
@@ -868,10 +872,12 @@ static int rdg_socket_read(BIO* bio, BYTE* pBuffer, size_t size,
rdg_http_encoding_context* encodingContext)
{
assert(encodingContext != NULL);
+
if (encodingContext->isWebsocketTransport)
{
return rdg_websocket_read(bio, pBuffer, size, &encodingContext->context.websocket);
}
+
switch (encodingContext->httpTransferEncoding)
{
case TransferEncodingIdentity:
@@ -881,7 +887,6 @@ static int rdg_socket_read(BIO* bio, BYTE* pBuffer, size_t size,
default:
return -1;
}
- return -1; /* should not be reached */
}
static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size,
@@ -893,7 +898,6 @@ static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size,
while (readCount < size)
{
int status = rdg_socket_read(tls->bio, pBuffer, size - readCount, transferEncoding);
-
if (status <= 0)
{
if (!BIO_should_retry(tls->bio))
@@ -1169,7 +1173,7 @@ static wStream* rdg_build_http_request(rdpRdg* rdg, const char* method,
goto out;
}
- http_request_set_transfer_encoding(request, transferEncoding);
+ http_request_set_transfer_encoding(request, transferEncoding);
s = http_request_write(rdg->http, request);
out:
@@ -1186,7 +1190,7 @@ static BOOL rdg_handle_ntlm_challenge(rdpNtlm* ntlm, HttpResponse* response)
BOOL continueNeeded = FALSE;
size_t len;
const char* token64 = NULL;
- int ntlmTokenLength = 0;
+ size_t ntlmTokenLength = 0;
BYTE* ntlmTokenData = NULL;
long StatusCode;
@@ -1852,7 +1856,6 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char*
rdg->transferEncoding.context.websocket.responseStreamBuffer = NULL;
return TRUE;
- break;
default:
return FALSE;
}
@@ -1899,6 +1902,9 @@ static BOOL rdg_tunnel_connect(rdpRdg* rdg)
if (!status)
{
+ assert(rdg);
+ assert(rdg->context);
+ assert(rdg->context->rdp);
rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED;
return FALSE;
}
@@ -1937,6 +1943,9 @@ BOOL rdg_connect(rdpRdg* rdg, DWORD timeout, BOOL* rpcFallback)
if (!status)
{
+ assert(rdg);
+ assert(rdg->context);
+ assert(rdg->context->rdp);
rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED;
return FALSE;
}
@@ -2013,7 +2022,7 @@ static int rdg_write_websocket_data_packet(rdpRdg* rdg, const BYTE* buf, int isi
/* mask as much as possible with 32bit access */
for (streamPos = 0; streamPos + 4 <= isize; streamPos += 4)
{
- uint32_t masked = *((uint32_t*)((BYTE*)buf + streamPos)) ^ maskingKey;
+ uint32_t masked = *((const uint32_t*)((const BYTE*)buf + streamPos)) ^ maskingKey;
Stream_Write_UINT32(sWS, masked);
}
@@ -2021,7 +2030,7 @@ static int rdg_write_websocket_data_packet(rdpRdg* rdg, const BYTE* buf, int isi
for (; streamPos < isize; streamPos++)
{
BYTE* partialMask = (BYTE*)(&maskingKey) + streamPos % 4;
- BYTE masked = *((BYTE*)((BYTE*)buf + streamPos)) ^ *partialMask;
+ BYTE masked = *((const BYTE*)((const BYTE*)buf + streamPos)) ^ *partialMask;
Stream_Write_UINT8(sWS, masked);
}
@@ -2068,7 +2077,10 @@ static int rdg_write_chunked_data_packet(rdpRdg* rdg, const BYTE* buf, int isize
len = Stream_Length(sChunk);
if (len > INT_MAX)
+ {
+ Stream_Free(sChunk, TRUE);
return -1;
+ }
status = tls_write_all(rdg->tlsIn, Stream_Buffer(sChunk), (int)len);
Stream_Free(sChunk, TRUE);
@@ -2089,8 +2101,6 @@ static int rdg_write_data_packet(rdpRdg* rdg, const BYTE* buf, int isize)
}
else
return rdg_write_chunked_data_packet(rdg, buf, isize);
-
- return -1;
}
static BOOL rdg_process_close_packet(rdpRdg* rdg, wStream* s)
@@ -2402,10 +2412,10 @@ static int rdg_bio_gets(BIO* bio, char* str, int size)
return -2;
}
-static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
+static long rdg_bio_ctrl(BIO* in_bio, int cmd, long arg1, void* arg2)
{
long status = -1;
- rdpRdg* rdg = (rdpRdg*)BIO_get_data(bio);
+ rdpRdg* rdg = (rdpRdg*)BIO_get_data(in_bio);
rdpTls* tlsOut = rdg->tlsOut;
rdpTls* tlsIn = rdg->tlsIn;
@@ -2422,42 +2432,42 @@ static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
}
else if (cmd == BIO_C_READ_BLOCKED)
{
- BIO* bio = tlsOut->bio;
- status = BIO_read_blocked(bio);
+ BIO* cbio = tlsOut->bio;
+ status = BIO_read_blocked(cbio);
}
else if (cmd == BIO_C_WRITE_BLOCKED)
{
- BIO* bio = tlsIn->bio;
+ BIO* cbio = tlsIn->bio;
if (rdg->transferEncoding.isWebsocketTransport)
- bio = tlsOut->bio;
+ cbio = tlsOut->bio;
- status = BIO_write_blocked(bio);
+ status = BIO_write_blocked(cbio);
}
else if (cmd == BIO_C_WAIT_READ)
{
int timeout = (int)arg1;
- BIO* bio = tlsOut->bio;
+ BIO* cbio = tlsOut->bio;
- if (BIO_read_blocked(bio))
- return BIO_wait_read(bio, timeout);
- else if (BIO_write_blocked(bio))
- return BIO_wait_write(bio, timeout);
+ if (BIO_read_blocked(cbio))
+ return BIO_wait_read(cbio, timeout);
+ else if (BIO_write_blocked(cbio))
+ return BIO_wait_write(cbio, timeout);
else
status = 1;
}
else if (cmd == BIO_C_WAIT_WRITE)
{
int timeout = (int)arg1;
- BIO* bio = tlsIn->bio;
+ BIO* cbio = tlsIn->bio;
if (rdg->transferEncoding.isWebsocketTransport)
- bio = tlsOut->bio;
+ cbio = tlsOut->bio;
- if (BIO_write_blocked(bio))
- status = BIO_wait_write(bio, timeout);
- else if (BIO_read_blocked(bio))
- status = BIO_wait_read(bio, timeout);
+ if (BIO_write_blocked(cbio))
+ status = BIO_wait_write(cbio, timeout);
+ else if (BIO_read_blocked(cbio))
+ status = BIO_wait_read(cbio, timeout);
else
status = 1;
}
@@ -2619,6 +2629,7 @@ void rdg_free(rdpRdg* rdg)
BIO_free_all(rdg->frontBio);
DeleteCriticalSection(&rdg->writeSection);
+
if (rdg->transferEncoding.isWebsocketTransport)
{
if (rdg->transferEncoding.context.websocket.responseStreamBuffer != NULL)
diff --git a/libfreerdp/core/gateway/rpc.c b/libfreerdp/core/gateway/rpc.c
index c5f85922e..4d6bf9222 100644
--- a/libfreerdp/core/gateway/rpc.c
+++ b/libfreerdp/core/gateway/rpc.c
@@ -24,6 +24,7 @@
#endif
#include <winpr/crt.h>
+#include <assert.h>
#include <winpr/tchar.h>
#include <winpr/synch.h>
#include <winpr/dsparse.h>
@@ -46,6 +47,7 @@
#include "rpc_client.h"
#include "rpc.h"
+#include "rts.h"
#define TAG FREERDP_TAG("core.gateway.rpc")
@@ -88,8 +90,10 @@ static const char* PTYPE_STRINGS[] = { "PTYPE_REQUEST", "PTYPE_PING",
*
*/
-void rpc_pdu_header_print(rpcconn_hdr_t* header)
+void rpc_pdu_header_print(const rpcconn_hdr_t* header)
{
+ assert(header);
+
WLog_INFO(TAG, "rpc_vers: %" PRIu8 "", header->common.rpc_vers);
WLog_INFO(TAG, "rpc_vers_minor: %" PRIu8 "", header->common.rpc_vers_minor);
@@ -139,26 +143,30 @@ void rpc_pdu_header_print(rpcconn_hdr_t* header)
}
}
-void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_common_hdr_t* header)
+rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc)
{
- header->rpc_vers = rpc->rpc_vers;
- header->rpc_vers_minor = rpc->rpc_vers_minor;
- header->packed_drep[0] = rpc->packed_drep[0];
- header->packed_drep[1] = rpc->packed_drep[1];
- header->packed_drep[2] = rpc->packed_drep[2];
- header->packed_drep[3] = rpc->packed_drep[3];
+ rpcconn_common_hdr_t header = { 0 };
+ assert(rpc);
+
+ header.rpc_vers = rpc->rpc_vers;
+ header.rpc_vers_minor = rpc->rpc_vers_minor;
+ header.packed_drep[0] = rpc->packed_drep[0];
+ header.packed_drep[1] = rpc->packed_drep[1];
+ header.packed_drep[2] = rpc->packed_drep[2];
+ header.packed_drep[3] = rpc->packed_drep[3];
+ return header;
}
-UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment)
+size_t rpc_offset_align(size_t* offset, size_t alignment)
{
- UINT32 pad;
+ size_t pad;
pad = *offset;
*offset = (*offset + alignment - 1) & ~(alignment - 1);
pad = *offset - pad;
return pad;
}
-UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad)
+size_t rpc_offset_pad(size_t* offset, size_t pad)
{
*offset += pad;
return pad;
@@ -239,64 +247,67 @@ UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad)
*
*/
-BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* length)
+BOOL rpc_get_stub_data_info(const rpcconn_hdr_t* header, size_t* poffset, size_t* length)
{
- UINT32 alloc_hint = 0;
- rpcconn_hdr_t* header;
+ size_t used = 0;
+ size_t offset = 0;
+ BOOL rc = FALSE;
UINT32 frag_length;
UINT32 auth_length;
- UINT32 auth_pad_length;
+ UINT32 auth_pad_length = 0;
UINT32 sec_trailer_offset;
- rpc_sec_trailer* sec_trailer;
- *offset = RPC_COMMON_FIELDS_LENGTH;
- header = ((rpcconn_hdr_t*)buffer);
+ const rpc_sec_trailer* sec_trailer = NULL;
+
+ assert(header);
+ assert(poffset);
+ assert(length);
+
+ offset = RPC_COMMON_FIELDS_LENGTH;
switch (header->common.ptype)
{
case PTYPE_RESPONSE:
- *offset += 8;
- rpc_offset_align(offset, 8);
- alloc_hint = header->response.alloc_hint;
+ offset += 8;
+ rpc_offset_align(&offset, 8);
+ sec_trailer = &header->response.auth_verifier;
break;
case PTYPE_REQUEST:
- *offset += 4;
- rpc_offset_align(offset, 8);
- alloc_hint = header->request.alloc_hint;
+ offset += 4;
+ rpc_offset_align(&offset, 8);
+ sec_trailer = &header->request.auth_verifier;
break;
case PTYPE_RTS:
- *offset += 4;
+ offset += 4;
break;
default:
WLog_ERR(TAG, "Unknown PTYPE: 0x%02" PRIX8 "", header->common.ptype);
- return FALSE;
+ goto fail;
}
- if (!length)
- return TRUE;
+ frag_length = header->common.frag_length;
+ auth_length = header->common.auth_length;
+
+ if (poffset)
+ *poffset = offset;
- if (header->common.ptype == PTYPE_REQUEST)
+ /* The fragment must be larger than the authentication trailer */
+ used = offset + auth_length + 8ull;
+ if (sec_trailer)
{
- UINT32 sec_trailer_offset;
- sec_trailer_offset = header->common.frag_length - header->common.auth_length - 8;
- *length = sec_trailer_offset - *offset;
- return TRUE;
+ auth_pad_length = sec_trailer->auth_pad_length;
+ used += sec_trailer->auth_pad_length;
}
- frag_length = header->common.frag_length;
- auth_length = header->common.auth_length;
+ if (frag_length < used)
+ goto fail;
+
+ if (!length)
+ return TRUE;
+
sec_trailer_offset = frag_length - auth_length - 8;
- sec_trailer = (rpc_sec_trailer*)&buffer[sec_trailer_offset];
- auth_pad_length = sec_trailer->auth_pad_length;
-#if 0
- WLog_DBG(TAG,
- "sec_trailer: type: %"PRIu8" level: %"PRIu8" pad_length: %"PRIu8" reserved: %"PRIu8" context_id: %"PRIu32"",
- sec_trailer->auth_type, sec_trailer->auth_level,
- sec_trailer->auth_pad_length, sec_trailer->auth_reserved,
- sec_trailer->auth_context_id);
-#endif
/**
* According to [MS-RPCE], auth_pad_length is the number of padding
@@ -310,18 +321,21 @@ BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* l
auth_length, (frag_length - (sec_trailer_offset + 8)));
}
- *length = frag_length - auth_length - 24 - 8 - auth_pad_length;
- return TRUE;
+ *length = sec_trailer_offset - auth_pad_length - offset;
+
+ rc = TRUE;
+fail:
+ return rc;
}
SSIZE_T rpc_channel_read(RpcChannel* channel, wStream* s, size_t length)
{
int status;
- if (!channel)
+ if (!channel || (length > INT32_MAX))
return -1;
- status = BIO_read(channel->tls->bio, Stream_Pointer(s), length);
+ status = BIO_read(channel->tls->bio, Stream_Pointer(s), (INT32)length);
if (status > 0)
{
@@ -340,10 +354,10 @@ SSIZE_T rpc_channel_write(RpcChannel* channel, const BYTE* data, size_t length)
{
int status;
- if (!channel)
+ if (!channel || (length > INT32_MAX))
return -1;
- status = tls_write_all(channel->tls, data, length);
+ status = tls_write_all(channel->tls, data, (INT32)length);
return status;
}
@@ -629,7 +643,7 @@ static void rpc_virtual_connection_free(RpcVirtualConnection* connection)
free(connection);
}
-static BOOL rpc_channel_tls_connect(RpcChannel* channel, int timeout)
+static BOOL rpc_channel_tls_connect(RpcChannel* channel, UINT32 timeout)
{
int sockfd;
rdpTls* tls;
@@ -719,7 +733,7 @@ static BOOL rpc_channel_tls_connect(RpcChannel* channel, int timeout)
return TRUE;
}
-static int rpc_in_channel_connect(RpcInChannel* inChannel, int timeout)
+static int rpc_in_channel_connect(RpcInChannel* inChannel, UINT32 timeout)
{
rdpContext* context;
@@ -814,7 +828,7 @@ int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout)
return 1;
}
-BOOL rpc_connect(rdpRpc* rpc, int timeout)
+BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout)
{
RpcInChannel* inChannel;
RpcOutChannel* outChannel;
@@ -840,7 +854,15 @@ BOOL rpc_connect(rdpRpc* rpc, int timeout)
rdpRpc* rpc_new(rdpTransport* transport)
{
- rdpRpc* rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc));
+ rdpContext* context;
+ rdpRpc* rpc;
+
+ assert(transport);
+
+ context = transport->context;
+ assert(context);
+
+ rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc));
if (!rpc)
return NULL;
@@ -848,7 +870,7 @@ rdpRpc* rpc_new(rdpTransport* transport)
rpc->State = RPC_CLIENT_STATE_INITIAL;
rpc->transport = transport;
rpc->settings = transport->settings;
- rpc->context = transport->context;
+ rpc->context = context;
rpc->SendSeqNum = 0;
rpc->ntlm = ntlm_new();
@@ -873,7 +895,7 @@ rdpRpc* rpc_new(rdpTransport* transport)
rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval;
rpc->CurrentKeepAliveTime = 0;
rpc->CallId = 2;
- rpc->client = rpc_client_new(rpc->context, rpc->max_recv_frag);
+ rpc->client = rpc_client_new(context, rpc->max_recv_frag);
if (!rpc->client)
goto out_free;
diff --git a/libfreerdp/core/gateway/rpc.h b/libfreerdp/core/gateway/rpc.h
index 1a58fe450..26eebf131 100644
--- a/libfreerdp/core/gateway/rpc.h
+++ b/libfreerdp/core/gateway/rpc.h
@@ -73,7 +73,6 @@ typedef struct _RPC_PDU
#include "../tcp.h"
#include "../transport.h"
-#include "rts.h"
#include "http.h"
#include "ntlm.h"
@@ -525,7 +524,8 @@ typedef struct
rpcconn_common_hdr_t header;
} rpcconn_shutdown_hdr_t;
-typedef union {
+typedef union
+{
rpcconn_common_hdr_t common;
rpcconn_alter_context_hdr_t alter_context;
rpcconn_alter_context_response_hdr_t alter_context_response;
@@ -767,14 +767,14 @@ struct rdp_rpc
RpcVirtualConnection* VirtualConnection;
};
-FREERDP_LOCAL void rpc_pdu_header_print(rpcconn_hdr_t* header);
-FREERDP_LOCAL void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_common_hdr_t* header);
+FREERDP_LOCAL void rpc_pdu_header_print(const rpcconn_hdr_t* header);
+FREERDP_LOCAL rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc);
-FREERDP_LOCAL UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment);
-FREERDP_LOCAL UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad);
+FREERDP_LOCAL size_t rpc_offset_align(size_t* offset, size_t alignment);
+FREERDP_LOCAL size_t rpc_offset_pad(size_t* offset, size_t pad);
-FREERDP_LOCAL BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* header, UINT32* offset,
- UINT32* length);
+FREERDP_LOCAL BOOL rpc_get_stub_data_info(const rpcconn_hdr_t* header, size_t* offset,
+ size_t* length);
FREERDP_LOCAL SSIZE_T rpc_channel_write(RpcChannel* channel, const BYTE* data, size_t length);
@@ -794,7 +794,7 @@ FREERDP_LOCAL BOOL rpc_virtual_connection_transition_to_state(rdpRpc* rpc,
RpcVirtualConnection* connection,
VIRTUAL_CONNECTION_STATE state);
-FREERDP_LOCAL BOOL rpc_connect(rdpRpc* rpc, int timeout);
+FREERDP_LOCAL BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout);
FREERDP_LOCAL rdpRpc* rpc_new(rdpTransport* transport);
FREERDP_LOCAL void rpc_free(rdpRpc* rpc);
diff --git a/libfreerdp/core/gateway/rpc_bind.c b/libfreerdp/core/gateway/rpc_bind.c
index 98ed9a9dc..bdf54d5a0 100644
--- a/libfreerdp/core/gateway/rpc_bind.c
+++ b/libfreerdp/core/gateway/rpc_bind.c
@@ -22,11 +22,14 @@
#endif
#include <winpr/crt.h>
+#include <assert.h>
#include <freerdp/log.h>
#include "rpc_client.h"
+#include "rts.h"
+
#include "rpc_bind.h"
#define TAG FREERDP_TAG("core.gateway.rpc")
@@ -106,18 +109,32 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
{
BOOL continueNeeded = FALSE;
int status = -1;
- BYTE* buffer = NULL;
+ wStream* buffer = NULL;
UINT32 offset;
- UINT32 length;
RpcClientCall* clientCall;
p_cont_elem_t* p_cont_elem;
- rpcconn_bind_hdr_t* bind_pdu = NULL;
+ rpcconn_bind_hdr_t bind_pdu = { 0 };
BOOL promptPassword = FALSE;
- rdpSettings* settings = rpc->settings;
- freerdp* instance = (freerdp*)settings->instance;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
- RpcInChannel* inChannel = connection->DefaultInChannel;
+ rdpSettings* settings;
+ freerdp* instance;
+ RpcVirtualConnection* connection;
+ RpcInChannel* inChannel;
const SecBuffer* sbuffer = NULL;
+
+ assert(rpc);
+
+ settings = rpc->settings;
+ assert(settings);
+
+ instance = (freerdp*)settings->instance;
+ assert(instance);
+
+ connection = rpc->VirtualConnection;
+
+ assert(connection);
+
+ inChannel = connection->DefaultInChannel;
+
WLog_DBG(TAG, "Sending Bind PDU");
ntlm_free(rpc->ntlm);
rpc->ntlm = ntlm_new();
@@ -180,36 +197,31 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
if (!continueNeeded)
goto fail;
- bind_pdu = (rpcconn_bind_hdr_t*)calloc(1, sizeof(rpcconn_bind_hdr_t));
-
- if (!bind_pdu)
- goto fail;
-
sbuffer = ntlm_client_get_output_buffer(rpc->ntlm);
if (!sbuffer)
goto fail;
- rpc_pdu_header_init(rpc, &bind_pdu->header);
- bind_pdu->header.auth_length = (UINT16)sbuffer->cbBuffer;
- bind_pdu->auth_verifier.auth_value = sbuffer->pvBuffer;
- bind_pdu->header.ptype = PTYPE_BIND;
- bind_pdu->header.pfc_flags =
+ bind_pdu.header = rpc_pdu_header_init(rpc);
+ bind_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer;
+ bind_pdu.auth_verifier.auth_value = sbuffer->pvBuffer;
+ bind_pdu.header.ptype = PTYPE_BIND;
+ bind_pdu.header.pfc_flags =
PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_SUPPORT_HEADER_SIGN | PFC_CONC_MPX;
- bind_pdu->header.call_id = 2;
- bind_pdu->max_xmit_frag = rpc->max_xmit_frag;
- bind_pdu->max_recv_frag = rpc->max_recv_frag;
- bind_pdu->assoc_group_id = 0;
- bind_pdu->p_context_elem.n_context_elem = 2;
- bind_pdu->p_context_elem.reserved = 0;
- bind_pdu->p_context_elem.reserved2 = 0;
- bind_pdu->p_context_elem.p_cont_elem =
- calloc(bind_pdu->p_context_elem.n_context_elem, sizeof(p_cont_elem_t));
-
- if (!bind_pdu->p_context_elem.p_cont_elem)
+ bind_pdu.header.call_id = 2;
+ bind_pdu.max_xmit_frag = rpc->max_xmit_frag;
+ bind_pdu.max_recv_frag = rpc->max_recv_frag;
+ bind_pdu.assoc_group_id = 0;
+ bind_pdu.p_context_elem.n_context_elem = 2;
+ bind_pdu.p_context_elem.reserved = 0;
+ bind_pdu.p_context_elem.reserved2 = 0;
+ bind_pdu.p_context_elem.p_cont_elem =
+ calloc(bind_pdu.p_context_elem.n_context_elem, sizeof(p_cont_elem_t));
+
+ if (!bind_pdu.p_context_elem.p_cont_elem)
goto fail;
- p_cont_elem = &bind_pdu->p_context_elem.p_cont_elem[0];
+ p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[0];
p_cont_elem->p_cont_id = 0;
p_cont_elem->n_transfer_syn = 1;
p_cont_elem->reserved = 0;
@@ -222,7 +234,7 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &NDR_UUID, sizeof(p_uuid_t));
p_cont_elem->transfer_syntaxes[0].if_version = NDR_SYNTAX_IF_VERSION;
- p_cont_elem = &bind_pdu->p_context_elem.p_cont_elem[1];
+ p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[1];
p_cont_elem->p_cont_id = 1;
p_cont_elem->n_transfer_syn = 1;
p_cont_elem->reserved = 0;
@@ -236,32 +248,23 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &BTFN_UUID, sizeof(p_uuid_t));
p_cont_elem->transfer_syntaxes[0].if_version = BTFN_SYNTAX_IF_VERSION;
offset = 116;
- bind_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4);
- bind_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT;
- bind_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
- bind_pdu->auth_verifier.auth_reserved = 0x00;
- bind_pdu->auth_verifier.auth_context_id = 0x00000000;
- offset += (8 + bind_pdu->header.auth_length);
- bind_pdu->header.frag_length = offset;
- buffer = (BYTE*)malloc(bind_pdu->header.frag_length);
+
+ bind_pdu.auth_verifier.auth_type = RPC_C_AUTHN_WINNT;
+ bind_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
+ bind_pdu.auth_verifier.auth_reserved = 0x00;
+ bind_pdu.auth_verifier.auth_context_id = 0x00000000;
+ offset += (8 + bind_pdu.header.auth_length);
+ bind_pdu.header.frag_length = offset;
+
+ buffer = Stream_New(NULL, bind_pdu.header.frag_length);
if (!buffer)
goto fail;
- CopyMemory(buffer, bind_pdu, 24);
- CopyMemory(&buffer[24], &bind_pdu->p_context_elem, 4);
- CopyMemory(&buffer[28], &bind_pdu->p_context_elem.p_cont_elem[0], 24);
- CopyMemory(&buffer[52], bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes, 20);
- CopyMemory(&buffer[72], &bind_pdu->p_context_elem.p_cont_elem[1], 24);
- CopyMemory(&buffer[96], bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes, 20);
- offset = 116;
- rpc_offset_pad(&offset, bind_pdu->auth_verifier.auth_pad_length);
- CopyMemory(&buffer[offset], &bind_pdu->auth_verifier.auth_type, 8);
- CopyMemory(&buffer[offset + 8], bind_pdu->auth_verifier.auth_value,
- bind_pdu->header.auth_length);
- offset += (8 + bind_pdu->header.auth_length);
- length = bind_pdu->header.frag_length;
- clientCall = rpc_client_call_new(bind_pdu->header.call_id, 0);
+ if (!rts_write_pdu_bind(buffer, &bind_pdu))
+ goto fail;
+
+ clientCall = rpc_client_call_new(bind_pdu.header.call_id, 0);
if (!clientCall)
goto fail;
@@ -272,22 +275,19 @@ int rpc_send_bind_pdu(rdpRpc* rpc)
goto fail;
}
- status = rpc_in_channel_send_pdu(inChannel, buffer, length);
+ Stream_SealLength(buffer);
+ status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer));
fail:
- if (bind_pdu)
+ if (bind_pdu.p_context_elem.p_cont_elem)
{
- if (bind_pdu->p_context_elem.p_cont_elem)
- {
- free(bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes);
- free(bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes);
- }
-
- free(bind_pdu->p_context_elem.p_cont_elem);
+ free(bind_pdu.p_context_elem.p_cont_elem[0].transfer_syntaxes);
+ free(bind_pdu.p_context_elem.p_cont_elem[1].transfer_syntaxes);
}
- free(bind_pdu);
- free(buffer);
+ free(bind_pdu.p_context_elem.p_cont_elem);
+
+ Stream_Free(buffer, TRUE);
return (status > 0) ? 1 : -1;
}
@@ -317,31 +317,47 @@ fail:
* example.
*/
-int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s)
{
+ BOOL rc = FALSE;
BOOL continueNeeded = FALSE;
- BYTE* auth_data;
- rpcconn_hdr_t* header;
- header = (rpcconn_hdr_t*)buffer;
+ const BYTE* auth_data;
+ size_t pos, end;
+ rpcconn_hdr_t header = { 0 };
+
+ assert(rpc);
+ assert(rpc->ntlm);
+ assert(s);
+
+ pos = Stream_GetPosition(s);
+ if (!rts_read_pdu_header(s, &header))
+ goto fail;
+
WLog_DBG(TAG, "Receiving BindAck PDU");
- if (!rpc || !rpc->ntlm)
- return -1;
+ rpc->max_recv_frag = header.bind_ack.max_xmit_frag;
+ rpc->max_xmit_frag = header.bind_ack.max_recv_frag;
- rpc->max_recv_frag = header->bind_ack.max_xmit_frag;
- rpc->max_xmit_frag = header->bind_ack.max_recv_frag;
- auth_data = buffer + (header->common.frag_length - header->common.auth_length);
+ /* Get the correct offset in the input data and pass that on as input buffer.
+ * rts_read_pdu_header did already do consistency checks */
+ end = Stream_GetPosition(s);
+ Stream_SetPosition(s, pos + header.common.frag_length - header.common.auth_length);
+ auth_data = Stream_Pointer(s);
+ Stream_SetPosition(s, end);
- if (!ntlm_client_set_input_buffer(rpc->ntlm, TRUE, auth_data, header->common.auth_length))
- return -1;
+ if (!ntlm_client_set_input_buffer(rpc->ntlm, TRUE, auth_data, header.common.auth_length))
+ goto fail;
if (!ntlm_authenticate(rpc->ntlm, &continueNeeded))
- return -1;
+ goto fail;
if (continueNeeded)
- return -1;
+ goto fail;
- return (int)length;
+ rc = TRUE;
+fail:
+ rts_free_pdu_header(&header, FALSE);
+ return rc;
}
/**
@@ -354,68 +370,63 @@ int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc)
{
int status = -1;
- BYTE* buffer;
- UINT32 offset;
- UINT32 length;
+ wStream* buffer;
+ size_t offset;
const SecBuffer* sbuffer;
RpcClientCall* clientCall;
- rpcconn_rpc_auth_3_hdr_t* auth_3_pdu;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
- RpcInChannel* inChannel = connection->DefaultInChannel;
- WLog_DBG(TAG, "Sending RpcAuth3 PDU");
- auth_3_pdu = (rpcconn_rpc_auth_3_hdr_t*)calloc(1, sizeof(rpcconn_rpc_auth_3_hdr_t));
+ rpcconn_rpc_auth_3_hdr_t auth_3_pdu = { 0 };
+ RpcVirtualConnection* connection;
+ RpcInChannel* inChannel;
- if (!auth_3_pdu)
- return -1;
+ assert(rpc);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
+ inChannel = connection->DefaultInChannel;
+ assert(inChannel);
+
+ WLog_DBG(TAG, "Sending RpcAuth3 PDU");
sbuffer = ntlm_client_get_output_buffer(rpc->ntlm);
if (!sbuffer)
- {
- free(auth_3_pdu);
return -1;
- }
- rpc_pdu_header_init(rpc, &auth_3_pdu->header);
- auth_3_pdu->header.auth_length = (UINT16)sbuffer->cbBuffer;
- auth_3_pdu->auth_verifier.auth_value = sbuffer->pvBuffer;
- auth_3_pdu->header.ptype = PTYPE_RPC_AUTH_3;
- auth_3_pdu->header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX;
- auth_3_pdu->header.call_id = 2;
- auth_3_pdu->max_xmit_frag = rpc->max_xmit_frag;
- auth_3_pdu->max_recv_frag = rpc->max_recv_frag;
+ auth_3_pdu.header = rpc_pdu_header_init(rpc);
+ auth_3_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer;
+ auth_3_pdu.auth_verifier.auth_value = sbuffer->pvBuffer;
+ auth_3_pdu.header.ptype = PTYPE_RPC_AUTH_3;
+ auth_3_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX;
+ auth_3_pdu.header.call_id = 2;
+ auth_3_pdu.max_xmit_frag = rpc->max_xmit_frag;
+ auth_3_pdu.max_recv_frag = rpc->max_recv_frag;
offset = 20;
- auth_3_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4);
- auth_3_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT;
- auth_3_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
- auth_3_pdu->auth_verifier.auth_reserved = 0x00;
- auth_3_pdu->auth_verifier.auth_context_id = 0x00000000;
- offset += (8 + auth_3_pdu->header.auth_length);
- auth_3_pdu->header.frag_length = offset;
- buffer = (BYTE*)malloc(auth_3_pdu->header.frag_length);
+ auth_3_pdu.auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4);
+ auth_3_pdu.auth_verifier.auth_type = RPC_C_AUTHN_WINNT;
+ auth_3_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
+ auth_3_pdu.auth_verifier.auth_reserved = 0x00;
+ auth_3_pdu.auth_verifier.auth_context_id = 0x00000000;
+ offset += (8 + auth_3_pdu.header.auth_length);
+ auth_3_pdu.header.frag_length = offset;
+
+ buffer = Stream_New(NULL, auth_3_pdu.header.frag_length);
if (!buffer)
- {
- free(auth_3_pdu);
return -1;
- }
- CopyMemory(buffer, auth_3_pdu, 20);
- offset = 20;
- rpc_offset_pad(&offset, auth_3_pdu->auth_verifier.auth_pad_length);
- CopyMemory(&buffer[offset], &auth_3_pdu->auth_verifier.auth_type, 8);
- CopyMemory(&buffer[offset + 8], auth_3_pdu->auth_verifier.auth_value,
- auth_3_pdu->header.auth_length);
- offset += (8 + auth_3_pdu->header.auth_length);
- length = auth_3_pdu->header.frag_length;
- clientCall = rpc_client_call_new(auth_3_pdu->header.call_id, 0);
+ if (!rts_write_pdu_auth3(buffer, &auth_3_pdu))
+ goto fail;
+
+ clientCall = rpc_client_call_new(auth_3_pdu.header.call_id, 0);
if (ArrayList_Add(rpc->client->ClientCallList, clientCall) >= 0)
{
- status = rpc_in_channel_send_pdu(inChannel, buffer, length);
+ Stream_SealLength(buffer);
+ status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer));
}
- free(auth_3_pdu);
- free(buffer);
+fail:
+ Stream_Free(buffer, TRUE);
return (status > 0) ? 1 : -1;
}
diff --git a/libfreerdp/core/gateway/rpc_bind.h b/libfreerdp/core/gateway/rpc_bind.h
index 759555f07..69758e521 100644
--- a/libfreerdp/core/gateway/rpc_bind.h
+++ b/libfreerdp/core/gateway/rpc_bind.h
@@ -35,7 +35,7 @@ FREERDP_LOCAL extern const p_uuid_t BTFN_UUID;
#define BTFN_SYNTAX_IF_VERSION 0x00000001
FREERDP_LOCAL int rpc_send_bind_pdu(rdpRpc* rpc);
-FREERDP_LOCAL int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length);
+FREERDP_LOCAL BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s);
FREERDP_LOCAL int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc);
#endif /* FREERDP_LIB_CORE_GATEWAY_RPC_BIND_H */
diff --git a/libfreerdp/core/gateway/rpc_client.c b/libfreerdp/core/gateway/rpc_client.c
index 845bbe4b8..fb32d3c5e 100644
--- a/libfreerdp/core/gateway/rpc_client.c
+++ b/libfreerdp/core/gateway/rpc_client.c
@@ -24,6 +24,7 @@
#include <freerdp/log.h>
#include <winpr/crt.h>
+#include <assert.h>
#include <winpr/print.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
@@ -35,6 +36,8 @@
#include "rpc_bind.h"
#include "rpc_fault.h"
#include "rpc_client.h"
+#include "rts_signature.h"
+
#include "../rdp.h"
#include "../proxy.h"
@@ -99,7 +102,7 @@ static int rpc_client_receive_pipe_write(RpcClient* client, const BYTE* buffer,
int rpc_client_receive_pipe_read(RpcClient* client, BYTE* buffer, size_t length)
{
int index = 0;
- int status = 0;
+ size_t status = 0;
int nchunks = 0;
DataChunk chunks[2];
@@ -122,7 +125,10 @@ int rpc_client_receive_pipe_read(RpcClient* client, BYTE* buffer, size_t length)
ResetEvent(client->PipeEvent);
LeaveCriticalSection(&(client->PipeLock));
- return status;
+
+ if (status > INT_MAX)
+ return -1;
+ return (int)status;
}
static int rpc_client_transition_to_state(rdpRpc* rpc, RPC_CLIENT_STATE state)
@@ -173,8 +179,15 @@ static int rpc_client_transition_to_state(rdpRpc* rpc, RPC_CLIENT_STATE state)
static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu)
{
int status = -1;
- rpcconn_rts_hdr_t* rts;
- rdpTsg* tsg = rpc->transport->tsg;
+ rdpTsg* tsg;
+
+ assert(rpc);
+ assert(pdu);
+
+ Stream_SealLength(pdu->s);
+ Stream_SetPosition(pdu->s, 0);
+
+ tsg = rpc->transport->tsg;
if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED)
{
@@ -187,17 +200,13 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu)
break;
case VIRTUAL_CONNECTION_STATE_WAIT_A3W:
- rts = (rpcconn_rts_hdr_t*)Stream_Buffer(pdu->s);
-
- if (!rts_match_pdu_signature(&RTS_PDU_CONN_A3_SIGNATURE, rts))
+ if (!rts_match_pdu_signature(&RTS_PDU_CONN_A3_SIGNATURE, pdu->s, NULL))
{
WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/A3");
return -1;
}
- status = rts_recv_CONN_A3_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));
-
- if (status < 0)
+ if (!rts_recv_CONN_A3_pdu(rpc, pdu->s))
{
WLog_ERR(TAG, "rts_recv_CONN_A3_pdu failure");
return -1;
@@ -209,17 +218,13 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu)
break;
case VIRTUAL_CONNECTION_STATE_WAIT_C2:
- rts = (rpcconn_rts_hdr_t*)Stream_Buffer(pdu->s);
-
- if (!rts_match_pdu_signature(&RTS_PDU_CONN_C2_SIGNATURE, rts))
+ if (!rts_match_pdu_signature(&RTS_PDU_CONN_C2_SIGNATURE, pdu->s, NULL))
{
WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/C2");
return -1;
}
- status = rts_recv_CONN_C2_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));
-
- if (status < 0)
+ if (!rts_recv_CONN_C2_pdu(rpc, pdu->s))
{
WLog_ERR(TAG, "rts_recv_CONN_C2_pdu failure");
return -1;
@@ -252,7 +257,7 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu)
{
if (pdu->Type == PTYPE_BIND_ACK)
{
- if (rpc_recv_bind_ack_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)) <= 0)
+ if (!rpc_recv_bind_ack_pdu(rpc, pdu->s))
{
WLog_ERR(TAG, "rpc_recv_bind_ack_pdu failure");
return -1;
@@ -301,90 +306,117 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu)
static int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment)
{
- BYTE* buffer;
+ int rc = -1;
RPC_PDU* pdu;
- UINT32 StubOffset;
- UINT32 StubLength;
+ size_t StubOffset;
+ size_t StubLength;
RpcClientCall* call;
- rpcconn_hdr_t* header;
+ rpcconn_hdr_t header = { 0 };
+
+ assert(rpc);
+ assert(rpc->client);
+ assert(fragment);
+
pdu = rpc->client->pdu;
- buffer = (BYTE*)Stream_Buffer(fragment);
- header = (rpcconn_hdr_t*)Stream_Buffer(fragment);
+ assert(pdu);
+
+ Stream_SealLength(fragment);
+ Stream_SetPosition(fragment, 0);
- if (header->common.ptype == PTYPE_RESPONSE)
+ if (!rts_read_pdu_header(fragment, &header))
+ goto fail;
+
+ if (header.common.ptype == PTYPE_RESPONSE)
{
- rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length;
+ rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header.common.frag_length;
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -=
- header->common.frag_length;
+ header.common.frag_length;
if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow <
(rpc->ReceiveWindow / 2))
{
- if (rts_send_flow_control_ack_pdu(rpc) < 0)
- return -1;
+ if (!rts_send_flow_control_ack_pdu(rpc))
+ goto fail;
}
- if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength))
+ if (!rpc_get_stub_data_info(&header, &StubOffset, &StubLength))
{
WLog_ERR(TAG, "expected stub");
- return -1;
+ goto fail;
}
if (StubLength == 4)
{
- if ((header->common.call_id == rpc->PipeCallId) &&
- (header->common.pfc_flags & PFC_LAST_FRAG))
+ if ((header.common.call_id == rpc->PipeCallId) &&
+ (header.common.pfc_flags & PFC_LAST_FRAG))
{
/* End of TsProxySetupReceivePipe */
TerminateEventArgs e;
- rpc->result = *((UINT32*)&buffer[StubOffset]);
- freerdp_abort_connect(rpc->context->instance);
- tsg_set_state(rpc->transport->tsg, TSG_STATE_TUNNEL_CLOSE_PENDING);
+ rdpContext* context = rpc->transport->context;
+ rdpTsg* tsg = rpc->transport->tsg;
+
+ assert(context);
+
+ if (Stream_Length(fragment) < StubOffset + 4)
+ goto fail;
+ Stream_SetPosition(fragment, StubOffset);
+ Stream_Read_UINT32(fragment, rpc->result);
+
+ freerdp_abort_connect(context->instance);
+ tsg_set_state(tsg, TSG_STATE_TUNNEL_CLOSE_PENDING);
EventArgsInit(&e, "freerdp");
e.code = 0;
- PubSub_OnTerminate(rpc->context->pubSub, rpc->context, &e);
- return 0;
+ PubSub_OnTerminate(context->pubSub, context, &e);
+ rc = 0;
+ goto success;
}
- if (header->common.call_id != rpc->PipeCallId)
+ if (header.common.call_id != rpc->PipeCallId)
{
/* Ignoring non-TsProxySetupReceivePipe Response */
- return 0;
+ rc = 0;
+ goto success;
}
}
if (rpc->StubFragCount == 0)
- rpc->StubCallId = header->common.call_id;
+ rpc->StubCallId = header.common.call_id;
- if (rpc->StubCallId != header->common.call_id)
+ if (rpc->StubCallId != header.common.call_id)
{
WLog_ERR(TAG,
"invalid call_id: actual: %" PRIu32 ", expected: %" PRIu32
", frag_count: %" PRIu32 "",
- rpc->StubCallId, header->common.call_id, rpc->StubFragCount);
+ rpc->StubCallId, header.common.call_id, rpc->StubFragCount);
}
call = rpc_client_call_find_by_id(rpc->client, rpc->StubCallId);
if (!call)
- return -1;
+ goto fail;
if (call->OpNum != TsProxySetupReceivePipeOpnum)
{
- if (!Stream_EnsureCapacity(pdu->s, header->response.alloc_hint))
- return -1;
+ const rpcconn_response_hdr_t* response =
+ (const rpcconn_response_hdr_t*)&header.response;
+ if (!Stream_EnsureCapacity(pdu->s, response->alloc_hint))
+ goto fail;
+
+ if (Stream_Length(fragment) < StubOffset + StubLength)
+ goto fail;
- Stream_Write(pdu->s, &buffer[StubOffset], StubLength);
+ Stream_SetPosition(fragment, StubOffset);
+ Stream_Write(pdu->s, Stream_Pointer(fragment), StubLength);
rpc->StubFragCount++;
- if (header->response.alloc_hint == StubLength)
+ if (response->alloc_hint == StubLength)
{
pdu->Flags = RPC_PDU_FLAG_STUB;
pdu->Type = PTYPE_RESPONSE;
pdu->CallId = rpc->StubCallId;
- Stream_SealLength(pdu->s);
+
if (rpc_client_recv_pdu(rpc, pdu) < 0)
- return -1;
+ goto fail;
rpc_pdu_reset(pdu);
rpc->StubFragCount = 0;
rpc->StubCallId = 0;
@@ -392,75 +424,84 @@ static int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment)
}
else
{
- rpc_client_receive_pipe_write(rpc->client, &buffer[StubOffset], (size_t)StubLength);
+ const rpcconn_response_hdr_t* response = &header.response;
+ if (Stream_Length(fragment) < StubOffset + StubLength)
+ goto fail;
+ Stream_SetPosition(fragment, StubOffset);
+ rpc_client_receive_pipe_write(rpc->client, Stream_Pointer(fragment),
+ (size_t)StubLength);
rpc->StubFragCount++;
- if (header->response.alloc_hint == StubLength)
+ if (response->alloc_hint == StubLength)
{
rpc->StubFragCount = 0;
rpc->StubCallId = 0;
}
}
- return 1;
+ goto success;
}
- else if (header->common.ptype == PTYPE_RTS)
+ else if (header.common.ptype == PTYPE_RTS)
{
if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED)
{
pdu->Flags = 0;
- pdu->Type = header->common.ptype;
- pdu->CallId = header->common.call_id;
+ pdu->Type = header.common.ptype;
+ pdu->CallId = header.common.call_id;
if (!Stream_EnsureCapacity(pdu->s, Stream_Length(fragment)))
- return -1;
+ goto fail;
- Stream_Write(pdu->s, buffer, Stream_Length(fragment));
- Stream_SealLength(pdu->s);
+ Stream_Write(pdu->s, Stream_Buffer(fragment), Stream_Length(fragment));
if (rpc_client_recv_pdu(rpc, pdu) < 0)
- return -1;
+ goto fail;
rpc_pdu_reset(pdu);
}
else
{
- if (rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length) < 0)
- return -1;
+ if (!rts_recv_out_of_sequence_pdu(rpc, fragment, &header))
+ goto fail;
}
- return 1;
+ goto success;
}
- else if (header->common.ptype == PTYPE_BIND_ACK)
+ else if (header.common.ptype == PTYPE_BIND_ACK)
{
pdu->Flags = 0;
- pdu->Type = header->common.ptype;
- pdu->CallId = header->common.call_id;
+ pdu->Type = header.common.ptype;
+ pdu->CallId = header.common.call_id;
if (!Stream_EnsureCapacity(pdu->s, Stream_Length(fragment)))
- return -1;
+ goto fail;
- Stream_Write(pdu->s, buffer, Stream_Length(fragment));
- Stream_SealLength(pdu->s);
+ Stream_Write(pdu->s, Stream_Buffer(fragment), Stream_Length(fragment));
if (rpc_client_recv_pdu(rpc, pdu) < 0)
- return -1;
+ goto fail;
rpc_pdu_reset(pdu);
- return 1;
+ goto success;
}
- else if (header->common.ptype == PTYPE_FAULT)
+ else if (header.common.ptype == PTYPE_FAULT)
{
- rpc_recv_fault_pdu(header->fault.status);
- return -1;
+ const rpcconn_fault_hdr_t* fault = (const rpcconn_fault_hdr_t*)&header.fault;
+ rpc_recv_fault_pdu(fault->status);
+ goto fail;
}
else
{
- WLog_ERR(TAG, "unexpected RPC PDU type 0x%02" PRIX8 "", header->common.ptype);
- return -1;
+ WLog_ERR(TAG, "unexpected RPC PDU type 0x%02" PRIX8 "", header.common.ptype);
+ goto fail;
}
- return 1;
+success:
+ rc = (rc < 0) ? 1 : 0; /* In case of default error return change to 1, otherwise we already set
+ the return code */
+fail:
+ rts_free_pdu_header(&header, FALSE);
+ return rc;
}
static int rpc_client_default_out_channel_recv(rdpRpc* rpc)
@@ -510,7 +551,7 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc)
/* Send CONN/A1 PDU over OUT channel */
- if (rts_send_CONN_A1_pdu(rpc) < 0)
+ if (!rts_send_CONN_A1_pdu(rpc))
{
http_response_free(response);
WLog_ERR(TAG, "rpc_send_CONN_A1_pdu error!");
@@ -550,7 +591,8 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc)
if (statusCode == HTTP_STATUS_DENIED)
{
- freerdp_set_last_error_if_not(rpc->context, FREERDP_ERROR_AUTHENTICATION_FAILED);
+ rdpContext* context = rpc->context;
+ freerdp_set_last_error_if_not(context, FREERDP_ERROR_AUTHENTICATION_FAILED);
}
http_response_free(response);
@@ -564,12 +606,13 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc)
}
else
{
- wStream* fragment;
- rpcconn_common_hdr_t* header;
- fragment = rpc->client->ReceiveFragment;
+ wStream* fragment = rpc->client->ReceiveFragment;
while (1)
{
+ size_t pos;
+ rpcconn_common_hdr_t header = { 0 };
+
while (Stream_GetPosition(fragment) < RPC_COMMON_FIELDS_LENGTH)
{
status = rpc_channel_read(&outChannel->common, fragment,
@@ -582,22 +625,27 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc)
return 0;
}
- header = (rpcconn_common_hdr_t*)Stream_Buffer(fragment);
+ pos = Stream_GetPosition(fragment);
+ Stream_SetPosition(fragment, 0);
- if (header->frag_length > rpc->max_recv_frag)
+ /* Ignore errors, the PDU might not be complete. */
+ rts_read_common_pdu_header(fragment, &header);
+ Stream_SetPosition(fragment, pos);
+
+ if (header.frag_length > rpc->max_recv_frag)
{
WLog_ERR(TAG,
"rpc_client_recv: invalid fragment size: %" PRIu16 " (max: %" PRIu16 ")",
- header->frag_length, rpc->max_recv_frag);
+ header.frag_length, rpc->max_recv_frag);
winpr_HexDump(TAG, WLOG_ERROR, Stream_Buffer(fragment),
Stream_GetPosition(fragment));
return -1;
}
- while (Stream_GetPosition(fragment) < header->frag_length)
+ while (Stream_GetPosition(fragment) < header.frag_length)
{
status = rpc_channel_read(&outChannel->common, fragment,
- header->frag_length - Stream_GetPosition(fragment));
+ header.frag_length - Stream_GetPosition(fragment));
if (status < 0)
{
@@ -605,14 +653,12 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc)
return -1;
}
- if (Stream_GetPosition(fragment) < header->frag_length)
+ if (Stream_GetPosition(fragment) < header.frag_length)
return 0;
}
{
/* complete fragment received */
- Stream_SealLength(fragment);
- Stream_SetPosition(fragment, 0);
status = rpc_client_recv_fragment(rpc, fragment);
if (status < 0)
@@ -664,10 +710,10 @@ static int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc)
if (rpc_ncacn_http_send_out_channel_request(&nextOutChannel->common, TRUE))
{
rpc_ncacn_http_ntlm_uninit(&nextOutChannel->common);
- status = rts_send_OUT_R1_A3_pdu(rpc);
- if (status >= 0)
+ if (rts_send_OUT_R1_A3_pdu(rpc))
{
+ status = 1;
rpc_out_channel_transition_to_state(
nextOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_A6W);
}
@@ -688,11 +734,14 @@ static int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc)
break;
+ case CLIENT_OUT_CHANNEL_STATE_INITIAL:
+ case CLIENT_OUT_CHANNEL_STATE_CONNECTED:
+ case CLIENT_OUT_CHANNEL_STATE_NEGOTIATED:
default:
WLog_ERR(TAG,
"rpc_client_nondefault_out_channel_recv: Unexpected message %08" PRIx32,
nextOutChannel->State);
- return -1;
+ status = -1;
}
http_response_free(response);
@@ -770,7 +819,7 @@ int rpc_client_in_channel_recv(rdpRpc* rpc)
/* Send CONN/B1 PDU over IN channel */
- if (rts_send_CONN_B1_pdu(rpc) < 0)
+ if (!rts_send_CONN_B1_pdu(rpc))
{
WLog_ERR(TAG, "rpc_send_CONN_B1_pdu error!");
http_response_free(response);
@@ -811,8 +860,8 @@ int rpc_client_in_channel_recv(rdpRpc* rpc)
RpcClientCall* rpc_client_call_find_by_id(RpcClient* client, UINT32 CallId)
{
- int index;
- int count;
+ size_t index;
+ size_t count;
RpcClientCall* clientCall = NULL;
if (!client)
@@ -857,18 +906,23 @@ static void rpc_array_client_call_free(void* call)
rpc_client_call_free((RpcClientCall*)call);
}
-int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length)
+int rpc_in_channel_send_pdu(RpcInChannel* inChannel, const BYTE* buffer, size_t length)
{
- int status;
+ SSIZE_T status;
RpcClientCall* clientCall;
- rpcconn_common_hdr_t* header;
+ wStream s;
+ rpcconn_common_hdr_t header = { 0 };
+
status = rpc_channel_write(&inChannel->common, buffer, length);
if (status <= 0)
return -1;
- header = (rpcconn_common_hdr_t*)buffer;
- clientCall = rpc_client_call_find_by_id(inChannel->common.client, header->call_id);
+ Stream_StaticInit(&s, buffer, length);
+ if (!rts_read_common_pdu_header(&s, &header))
+ return -1;
+
+ clientCall = rpc_client_call_find_by_id(inChannel->common.client, header.call_id);
clientCall->State = RPC_CLIENT_CALL_STATE_DISPATCHED;
/*
@@ -878,7 +932,7 @@ int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length
* variables specified by this abstract data model.
*/
- if (header->ptype == PTYPE_REQUEST)
+ if (header.ptype == PTYPE_REQUEST)
{
inChannel->BytesSent += status;
inChannel->SenderAvailableWindow -= status;
@@ -889,7 +943,7 @@ int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length
BOOL rpc_client_write_call(rdpRpc* rpc, wStream* s, UINT16 opnum)
{
- UINT32 offset;
+ size_t offset;
BYTE* buffer = NULL;
UINT32 stub_data_pad;
SecBuffer Buffers[2] = { 0 };
@@ -937,7 +991,7 @@ BOOL rpc_client_write_call(rdpRpc* rpc, wStream* s, UINT16 opnum)
if (size < 0)
goto fail;
- rpc_pdu_header_init(rpc, &request_pdu.header);
+ request_pdu.header = rpc_pdu_header_init(rpc);
request_pdu.header.ptype = PTYPE_REQUEST;
request_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
request_pdu.header.auth_length = (UINT16)size;
@@ -1026,7 +1080,7 @@ static BOOL rpc_client_resolve_gateway(rdpSettings* settings, char** host, UINT1
const char* peerHostname = freerdp_settings_get_string(settings, FreeRDP_GatewayHostname);
const char* proxyUsername = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
const char* proxyPassword = freerdp_settings_get_string(settings, FreeRDP_GatewayPassword);
- *port = freerdp_settings_get_uint32(settings, FreeRDP_GatewayPort);
+ *port = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_GatewayPort);
*isProxy = proxy_prepare(settings, &peerHostname, port, &proxyUsername, &proxyPassword);
result = freerdp_tcp_resolve_host(peerHostname, *port, 0);
@@ -1084,9 +1138,6 @@ RpcClient* rpc_client_new(rdpContext* context, UINT32 max_recv_frag)
goto fail;
obj = ArrayList_Object(client->ClientCallList);
- if (!obj)
- goto fail;
-
obj->fnObjectFree = rpc_array_client_call_free;
return client;
fail:
diff --git a/libfreerdp/core/gateway/rpc_client.h b/libfreerdp/core/gateway/rpc_client.h
index af0b8ce13..7b509def0 100644
--- a/libfreerdp/core/gateway/rpc_client.h
+++ b/libfreerdp/core/gateway/rpc_client.h
@@ -31,7 +31,8 @@ FREERDP_LOCAL RpcClientCall* rpc_client_call_find_by_id(RpcClient* client, UINT3
FREERDP_LOCAL RpcClientCall* rpc_client_call_new(UINT32 CallId, UINT32 OpNum);
FREERDP_LOCAL void rpc_client_call_free(RpcClientCall* client_call);
-FREERDP_LOCAL int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length);
+FREERDP_LOCAL int rpc_in_channel_send_pdu(RpcInChannel* inChannel, const BYTE* buffer,
+ size_t length);
FREERDP_LOCAL int rpc_client_in_channel_recv(rdpRpc* rpc);
FREERDP_LOCAL int rpc_client_out_channel_recv(rdpRpc* rpc);
diff --git a/libfreerdp/core/gateway/rpc_fault.c b/libfreerdp/core/gateway/rpc_fault.c
index dcf59d62b..12d466cd9 100644
--- a/libfreerdp/core/gateway/rpc_fault.c
+++ b/libfreerdp/core/gateway/rpc_fault.c
@@ -154,8 +154,7 @@ static const RPC_FAULT_CODE RPC_FAULT_CODES[] = {
DEFINE_RPC_FAULT_CODE(RPC_S_COOKIE_AUTH_FAILED, CAT_GATEWAY),
DEFINE_RPC_FAULT_CODE(RPC_S_GROUP_MEMBER_NOT_FOUND, CAT_GATEWAY),
DEFINE_RPC_FAULT_CODE(EPT_S_CANT_CREATE, CAT_GATEWAY),
- DEFINE_RPC_FAULT_CODE(RPC_S_INVALID_OBJECT, CAT_GATEWAY),
- { 0, NULL, NULL }
+ DEFINE_RPC_FAULT_CODE(RPC_S_INVALID_OBJECT, CAT_GATEWAY)
};
static const RPC_FAULT_CODE RPC_TSG_FAULT_CODES[] = {
@@ -207,8 +206,7 @@ static const RPC_FAULT_CODE RPC_TSG_FAULT_CODES[] = {
{ HRESULT_CODE(E_PROXY_REAUTH_NAP_FAILED), "E_PROXY_REAUTH_NAP_FAILED", CAT_GATEWAY },
{ HRESULT_CODE(E_PROXY_CONNECTIONABORTED), "E_PROXY_CONNECTIONABORTED", CAT_GATEWAY },
{ HRESULT_CODE(E_PROXY_NOCERTAVAILABLE), "E_PROXY_NOCERTAVAILABLE", CAT_GATEWAY },
- { HRESULT_CODE(RPC_S_CALL_CANCELLED), "RPC_S_CALL_CANCELLED", CAT_GATEWAY },
- { 0, NULL, NULL }
+ { HRESULT_CODE(RPC_S_CALL_CANCELLED), "RPC_S_CALL_CANCELLED", CAT_GATEWAY }
};
/**
@@ -361,29 +359,32 @@ const char* rpc_error_to_string(UINT32 code)
size_t index;
static char buffer[1024];
- for (index = 0; RPC_FAULT_CODES[index].name != NULL; index++)
+ for (index = 0; index < ARRAYSIZE(RPC_FAULT_CODES); index++)
{
- if (RPC_FAULT_CODES[index].code == code)
+ const RPC_FAULT_CODE* const current = &RPC_FAULT_CODES[index];
+ if (current->code == code)
{
- sprintf_s(buffer, ARRAYSIZE(buffer), "%s", RPC_FAULT_CODES[index].name);
+ sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name);
goto out;
}
}
- for (index = 0; RPC_TSG_FAULT_CODES[index].name != NULL; index++)
+ for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++)
{
- if (RPC_TSG_FAULT_CODES[index].code == code)
+ const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index];
+ if (current->code == code)
{
- sprintf_s(buffer, ARRAYSIZE(buffer), "%s", RPC_TSG_FAULT_CODES[index].name);
+ sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name);
goto out;
}
}
- for (index = 0; RPC_TSG_FAULT_CODES[index].name != NULL; index++)
+ for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++)
{
- if (RPC_TSG_FAULT_CODES[index].code == HRESULT_CODE(code))
+ const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index];
+ if (current->code == HRESULT_CODE(code))
{
- sprintf_s(buffer, ARRAYSIZE(buffer), "%s", RPC_TSG_FAULT_CODES[index].name);
+ sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name);
goto out;
}
}
@@ -397,22 +398,25 @@ const char* rpc_error_to_category(UINT32 code)
{
size_t index;
- for (index = 0; RPC_FAULT_CODES[index].category != NULL; index++)
+ for (index = 0; index < ARRAYSIZE(RPC_FAULT_CODES); index++)
{
- if (RPC_FAULT_CODES[index].code == code)
- return RPC_FAULT_CODES[index].category;
+ const RPC_FAULT_CODE* const current = &RPC_FAULT_CODES[index];
+ if (current->code == code)
+ return current->category;
}
- for (index = 0; RPC_TSG_FAULT_CODES[index].category != NULL; index++)
+ for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++)
{
- if (RPC_TSG_FAULT_CODES[index].code == code)
- return RPC_TSG_FAULT_CODES[index].category;
+ const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index];
+ if (current->code == code)
+ return current->category;
}
- for (index = 0; RPC_TSG_FAULT_CODES[index].category != NULL; index++)
+ for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++)
{
- if (RPC_TSG_FAULT_CODES[index].code == HRESULT_CODE(code))
- return RPC_TSG_FAULT_CODES[index].category;
+ const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index];
+ if (current->code == HRESULT_CODE(code))
+ return current->category;
}
return "UNKNOWN";
diff --git a/libfreerdp/core/gateway/rts.c b/libfreerdp/core/gateway/rts.c
index c003b1ea0..ccbe0beca 100644
--- a/libfreerdp/core/gateway/rts.c
+++ b/libfreerdp/core/gateway/rts.c
@@ -21,6 +21,7 @@
#include "config.h"
#endif
+#include <assert.h>
#include <winpr/crt.h>
#include <winpr/crypto.h>
#include <winpr/winhttp.h>
@@ -29,6 +30,7 @@
#include "ncacn_http.h"
#include "rpc_client.h"
+#include "rts_signature.h"
#include "rts.h"
@@ -67,6 +69,56 @@
*
*/
+static const char* rts_pdu_ptype_to_string(UINT32 ptype)
+{
+ switch (ptype)
+ {
+ case PTYPE_REQUEST:
+ return "PTYPE_REQUEST";
+ case PTYPE_PING:
+ return "PTYPE_PING";
+ case PTYPE_RESPONSE:
+ return "PTYPE_RESPONSE";
+ case PTYPE_FAULT:
+ return "PTYPE_FAULT";
+ case PTYPE_WORKING:
+ return "PTYPE_WORKING";
+ case PTYPE_NOCALL:
+ return "PTYPE_NOCALL";
+ case PTYPE_REJECT:
+ return "PTYPE_REJECT";
+ case PTYPE_ACK:
+ return "PTYPE_ACK";
+ case PTYPE_CL_CANCEL:
+ return "PTYPE_CL_CANCEL";
+ case PTYPE_FACK:
+ return "PTYPE_FACK";
+ case PTYPE_CANCEL_ACK:
+ return "PTYPE_CANCEL_ACK";
+ case PTYPE_BIND:
+ return "PTYPE_BIND";
+ case PTYPE_BIND_ACK:
+ return "PTYPE_BIND_ACK";
+ case PTYPE_BIND_NAK:
+ return "PTYPE_BIND_NAK";
+ case PTYPE_ALTER_CONTEXT:
+ return "PTYPE_ALTER_CONTEXT";
+ case PTYPE_ALTER_CONTEXT_RESP:
+ return "PTYPE_ALTER_CONTEXT_RESP";
+ case PTYPE_RPC_AUTH_3:
+ return "PTYPE_RPC_AUTH_3";
+ case PTYPE_SHUTDOWN:
+ return "PTYPE_SHUTDOWN";
+ case PTYPE_CO_CANCEL:
+ return "PTYPE_CO_CANCEL";
+ case PTYPE_ORPHANED:
+ return "PTYPE_ORPHANED";
+ case PTYPE_RTS:
+ return "PTYPE_RTS";
+ default:
+ return "UNKNOWN";
+ }
+}
static rpcconn_rts_hdr_t rts_pdu_header_init(void)
{
rpcconn_rts_hdr_t header = { 0 };
@@ -84,528 +136,1576 @@ static rpcconn_rts_hdr_t rts_pdu_header_init(void)
return header;
}
-static int rts_receive_window_size_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length,
- UINT32* ReceiveWindowSize)
+static BOOL rts_align_stream(wStream* s, size_t alignment)
{
- if (ReceiveWindowSize)
- *ReceiveWindowSize = *((UINT32*)&buffer[0]); /* ReceiveWindowSize (4 bytes) */
+ size_t pos, pad;
- return 4;
+ assert(s);
+ assert(alignment > 0);
+
+ pos = Stream_GetPosition(s);
+ pad = rpc_offset_align(&pos, alignment);
+ return Stream_SafeSeek(s, pad);
+}
+
+static char* sdup(const void* src, size_t length)
+{
+ char* dst;
+ assert(src || (length == 0));
+ if (length == 0)
+ return NULL;
+
+ dst = calloc(length + 1, sizeof(char));
+ if (!dst)
+ return NULL;
+ memcpy(dst, src, length);
+ return dst;
+}
+
+static BOOL rts_write_common_pdu_header(wStream* s, const rpcconn_common_hdr_t* header)
+{
+ assert(s);
+ assert(header);
+ if (!Stream_EnsureRemainingCapacity(s, sizeof(rpcconn_common_hdr_t)))
+ return FALSE;
+
+ Stream_Write_UINT8(s, header->rpc_vers);
+ Stream_Write_UINT8(s, header->rpc_vers_minor);
+ Stream_Write_UINT8(s, header->ptype);
+ Stream_Write_UINT8(s, header->pfc_flags);
+ Stream_Write(s, header->packed_drep, ARRAYSIZE(header->packed_drep));
+ Stream_Write_UINT16(s, header->frag_length);
+ Stream_Write_UINT16(s, header->auth_length);
+ Stream_Write_UINT32(s, header->call_id);
+ return TRUE;
+}
+
+BOOL rts_read_common_pdu_header(wStream* s, rpcconn_common_hdr_t* header)
+{
+ size_t left;
+ assert(s);
+ assert(header);
+
+ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+
+ Stream_Read_UINT8(s, header->rpc_vers);
+ Stream_Read_UINT8(s, header->rpc_vers_minor);
+ Stream_Read_UINT8(s, header->ptype);
+ Stream_Read_UINT8(s, header->pfc_flags);
+ Stream_Read(s, header->packed_drep, ARRAYSIZE(header->packed_drep));
+ Stream_Read_UINT16(s, header->frag_length);
+ Stream_Read_UINT16(s, header->auth_length);
+ Stream_Read_UINT32(s, header->call_id);
+
+ if (header->frag_length < sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+
+ left = Stream_GetRemainingLength(s);
+ if (left < header->frag_length - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+
+ return TRUE;
}
-static int rts_receive_window_size_command_write(BYTE* buffer, UINT32 ReceiveWindowSize)
+static BOOL rts_read_auth_verifier_no_checks(wStream* s, auth_verifier_co_t* auth,
+ const rpcconn_common_hdr_t* header, size_t* startPos)
{
- if (buffer)
+ assert(s);
+ assert(auth);
+ assert(header);
+
+ assert(header->frag_length > header->auth_length);
+
+ if (startPos)
+ *startPos = Stream_GetPosition(s);
+
+ /* Read the auth verifier and check padding matches frag_length */
+ {
+ const size_t expected = header->frag_length - header->auth_length - 8;
+
+ Stream_SetPosition(s, expected);
+ if (Stream_GetRemainingLength(s) < sizeof(auth_verifier_co_t))
+ return FALSE;
+
+ Stream_Read_UINT8(s, auth->auth_type);
+ Stream_Read_UINT8(s, auth->auth_level);
+ Stream_Read_UINT8(s, auth->auth_pad_length);
+ Stream_Read_UINT8(s, auth->auth_reserved);
+ Stream_Read_UINT32(s, auth->auth_context_id);
+ }
+
+ if (header->auth_length != 0)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_RECEIVE_WINDOW_SIZE; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = ReceiveWindowSize; /* ReceiveWindowSize (4 bytes) */
+ const void* ptr = Stream_Pointer(s);
+ if (!Stream_SafeSeek(s, header->auth_length))
+ return FALSE;
+ auth->auth_value = (BYTE*)sdup(ptr, header->auth_length);
+ if (auth->auth_value == NULL)
+ return FALSE;
}
- return 8;
+ return TRUE;
}
-static int rts_flow_control_ack_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length,
- UINT32* BytesReceived, UINT32* AvailableWindow,
- BYTE* ChannelCookie)
+static BOOL rts_read_auth_verifier(wStream* s, auth_verifier_co_t* auth,
+ const rpcconn_common_hdr_t* header)
{
- /* Ack (24 bytes) */
- if (BytesReceived)
- *BytesReceived = *((UINT32*)&buffer[0]); /* BytesReceived (4 bytes) */
+ size_t pos;
+ assert(s);
+ assert(auth);
+ assert(header);
- if (AvailableWindow)
- *AvailableWindow = *((UINT32*)&buffer[4]); /* AvailableWindow (4 bytes) */
+ if (!rts_read_auth_verifier_no_checks(s, auth, header, &pos))
+ return FALSE;
- if (ChannelCookie)
- CopyMemory(ChannelCookie, &buffer[8], 16); /* ChannelCookie (16 bytes) */
+ {
+ const size_t expected = header->frag_length - header->auth_length - 8;
+ assert(pos + auth->auth_pad_length == expected);
+ }
- return 24;
+ return TRUE;
}
-static int rts_flow_control_ack_command_write(BYTE* buffer, UINT32 BytesReceived,
- UINT32 AvailableWindow, BYTE* ChannelCookie)
+static BOOL rts_read_auth_verifier_with_stub(wStream* s, auth_verifier_co_t* auth,
+ rpcconn_common_hdr_t* header)
{
- if (buffer)
+ size_t pos;
+ size_t alloc_hint = 0;
+ BYTE** ptr = NULL;
+
+ if (!rts_read_auth_verifier_no_checks(s, auth, header, &pos))
+ return FALSE;
+
+ switch (header->ptype)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_FLOW_CONTROL_ACK; /* CommandType (4 bytes) */
- /* Ack (24 bytes) */
- *((UINT32*)&buffer[4]) = BytesReceived; /* BytesReceived (4 bytes) */
- *((UINT32*)&buffer[8]) = AvailableWindow; /* AvailableWindow (4 bytes) */
- CopyMemory(&buffer[12], ChannelCookie, 16); /* ChannelCookie (16 bytes) */
+ case PTYPE_FAULT:
+ {
+ rpcconn_fault_hdr_t* hdr = (rpcconn_fault_hdr_t*)header;
+ alloc_hint = hdr->alloc_hint;
+ ptr = &hdr->stub_data;
+ }
+ break;
+ case PTYPE_RESPONSE:
+ {
+ rpcconn_response_hdr_t* hdr = (rpcconn_response_hdr_t*)header;
+ alloc_hint = hdr->alloc_hint;
+ ptr = &hdr->stub_data;
+ }
+ break;
+ case PTYPE_REQUEST:
+ {
+ rpcconn_request_hdr_t* hdr = (rpcconn_request_hdr_t*)header;
+ alloc_hint = hdr->alloc_hint;
+ ptr = &hdr->stub_data;
+ }
+ break;
+ default:
+ return FALSE;
}
- return 28;
+ if (alloc_hint > 0)
+ {
+ const size_t size =
+ header->frag_length - header->auth_length - 8 - auth->auth_pad_length - pos;
+ const void* src = Stream_Buffer(s) + pos;
+
+ *ptr = (BYTE*)sdup(src, size);
+ if (!*ptr)
+ return FALSE;
+ }
+
+ return TRUE;
}
-static int rts_connection_timeout_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length,
- UINT32* ConnectionTimeout)
+static void rts_free_auth_verifier(auth_verifier_co_t* auth)
{
- if (ConnectionTimeout)
- *ConnectionTimeout = *((UINT32*)&buffer[0]); /* ConnectionTimeout (4 bytes) */
-
- return 4;
+ if (!auth)
+ return;
+ free(auth->auth_value);
}
-static int rts_connection_timeout_command_write(BYTE* buffer, UINT32 ConnectionTimeout)
+static BOOL rts_write_auth_verifier(wStream* s, const auth_verifier_co_t* auth,
+ const rpcconn_common_hdr_t* header)
{
- if (buffer)
+ size_t pos;
+ UINT8 auth_pad_length = 0;
+
+ assert(s);
+ assert(auth);
+ assert(header);
+
+ /* Align start to a multiple of 4 */
+ pos = Stream_GetPosition(s);
+ if ((pos % 4) != 0)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_CONNECTION_TIMEOUT; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = ConnectionTimeout; /* ConnectionTimeout (4 bytes) */
+ auth_pad_length = 4 - (pos % 4);
+ if (!Stream_EnsureRemainingCapacity(s, auth_pad_length))
+ return FALSE;
+ Stream_Zero(s, auth_pad_length);
}
- return 8;
+ assert(header->frag_length + 8ull > header->auth_length);
+ {
+ size_t pos = Stream_GetPosition(s);
+ size_t expected = header->frag_length - header->auth_length - 8;
+
+ assert(pos == expected);
+ }
+
+ if (!Stream_EnsureRemainingCapacity(s, sizeof(auth_verifier_co_t)))
+ return FALSE;
+
+ Stream_Write_UINT8(s, auth->auth_type);
+ Stream_Write_UINT8(s, auth->auth_level);
+ Stream_Write_UINT8(s, auth_pad_length);
+ Stream_Write_UINT8(s, 0); /* auth->auth_reserved */
+ Stream_Write_UINT32(s, auth->auth_context_id);
+
+ if (!Stream_EnsureRemainingCapacity(s, header->auth_length))
+ return FALSE;
+ Stream_Write(s, auth->auth_value, header->auth_length);
+ return TRUE;
+}
+
+static BOOL rts_read_version(wStream* s, p_rt_version_t* version)
+{
+ assert(s);
+ assert(version);
+
+ if (Stream_GetRemainingLength(s) < 2 * sizeof(UINT8))
+ return FALSE;
+ Stream_Read_UINT8(s, version->major);
+ Stream_Read_UINT8(s, version->minor);
+ return TRUE;
}
-static int rts_cookie_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+void rts_free_supported_versions(p_rt_versions_supported_t* versions)
{
- /* Cookie (16 bytes) */
- return 16;
+ if (!versions)
+ return;
+ free(versions->p_protocols);
+ versions->p_protocols = NULL;
}
-static int rts_cookie_command_write(BYTE* buffer, BYTE* Cookie)
+static BOOL rts_read_supported_versions(wStream* s, p_rt_versions_supported_t* versions)
{
- if (buffer)
+ BYTE x;
+
+ assert(s);
+ assert(versions);
+
+ if (Stream_GetRemainingLength(s) < sizeof(UINT8))
+ return FALSE;
+
+ Stream_Read_UINT8(s, versions->n_protocols); /* count */
+
+ if (versions->n_protocols > 0)
+ {
+ versions->p_protocols = calloc(versions->n_protocols, sizeof(p_rt_version_t));
+ if (!versions->p_protocols)
+ return FALSE;
+ }
+ for (x = 0; x < versions->n_protocols; x++)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_COOKIE; /* CommandType (4 bytes) */
- CopyMemory(&buffer[4], Cookie, 16); /* Cookie (16 bytes) */
+ p_rt_version_t* version = &versions->p_protocols[x];
+ if (!rts_read_version(s, version)) /* size_is(n_protocols) */
+ {
+ rts_free_supported_versions(versions);
+ return FALSE;
+ }
}
- return 20;
+ return TRUE;
}
-static int rts_channel_lifetime_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static BOOL rts_read_port_any(wStream* s, port_any_t* port)
{
- /* ChannelLifetime (4 bytes) */
- return 4;
+ const void* ptr;
+
+ assert(s);
+ assert(port);
+
+ if (Stream_GetRemainingLength(s) < sizeof(UINT16))
+ return FALSE;
+
+ Stream_Read_UINT16(s, port->length);
+ if (port->length == 0)
+ return TRUE;
+
+ ptr = Stream_Pointer(s);
+ if (!Stream_SafeSeek(s, port->length))
+ return FALSE;
+ port->port_spec = sdup(ptr, port->length);
+ return port->port_spec != NULL;
}
-static int rts_channel_lifetime_command_write(BYTE* buffer, UINT32 ChannelLifetime)
+static void rts_free_port_any(port_any_t* port)
{
- if (buffer)
- {
- *((UINT32*)&buffer[0]) = RTS_CMD_CHANNEL_LIFETIME; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = ChannelLifetime; /* ChannelLifetime (4 bytes) */
- }
+ if (!port)
+ return;
+ free(port->port_spec);
+}
- return 8;
+static BOOL rts_read_uuid(wStream* s, p_uuid_t* uuid)
+{
+ assert(s);
+ assert(uuid);
+
+ if (Stream_GetRemainingLength(s) < sizeof(p_uuid_t))
+ return FALSE;
+
+ Stream_Read_UINT32(s, uuid->time_low);
+ Stream_Read_UINT16(s, uuid->time_mid);
+ Stream_Read_UINT16(s, uuid->time_hi_and_version);
+ Stream_Read_UINT8(s, uuid->clock_seq_hi_and_reserved);
+ Stream_Read_UINT8(s, uuid->clock_seq_low);
+ Stream_Read(s, uuid->node, ARRAYSIZE(uuid->node));
+ return TRUE;
}
-static int rts_client_keepalive_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static BOOL rts_write_uuid(wStream* s, const p_uuid_t* uuid)
{
- /* ClientKeepalive (4 bytes) */
- return 4;
+ assert(s);
+ assert(uuid);
+
+ if (!Stream_EnsureRemainingCapacity(s, sizeof(p_uuid_t)))
+ return FALSE;
+
+ Stream_Write_UINT32(s, uuid->time_low);
+ Stream_Write_UINT16(s, uuid->time_mid);
+ Stream_Write_UINT16(s, uuid->time_hi_and_version);
+ Stream_Write_UINT8(s, uuid->clock_seq_hi_and_reserved);
+ Stream_Write_UINT8(s, uuid->clock_seq_low);
+ Stream_Write(s, uuid->node, ARRAYSIZE(uuid->node));
+ return TRUE;
}
-static int rts_client_keepalive_command_write(BYTE* buffer, UINT32 ClientKeepalive)
+static p_syntax_id_t* rts_syntax_id_new(size_t count)
{
- /**
- * An unsigned integer that specifies the keep-alive interval, in milliseconds,
- * that this connection is configured to use. This value MUST be 0 or in the inclusive
- * range of 60,000 through 4,294,967,295. If it is 0, it MUST be interpreted as 300,000.
- */
- if (buffer)
- {
- *((UINT32*)&buffer[0]) = RTS_CMD_CLIENT_KEEPALIVE; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = ClientKeepalive; /* ClientKeepalive (4 bytes) */
- }
+ return calloc(count, sizeof(p_syntax_id_t));
+}
- return 8;
+static void rts_syntax_id_free(p_syntax_id_t* ptr)
+{
+ free(ptr);
}
-static int rts_version_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static BOOL rts_read_syntax_id(wStream* s, p_syntax_id_t* syntax_id)
{
- /* Version (4 bytes) */
- return 4;
+ assert(s);
+ assert(syntax_id);
+
+ if (!rts_read_uuid(s, &syntax_id->if_uuid))
+ return FALSE;
+
+ if (Stream_GetRemainingLength(s) < 4)
+ return FALSE;
+
+ Stream_Read_UINT32(s, syntax_id->if_version);
+ return TRUE;
}
-static int rts_version_command_write(BYTE* buffer)
+static BOOL rts_write_syntax_id(wStream* s, const p_syntax_id_t* syntax_id)
{
- if (buffer)
- {
- *((UINT32*)&buffer[0]) = RTS_CMD_VERSION; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = 1; /* Version (4 bytes) */
- }
+ assert(s);
+ assert(syntax_id);
+
+ if (!rts_write_uuid(s, &syntax_id->if_uuid))
+ return FALSE;
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return FALSE;
+
+ Stream_Write_UINT32(s, syntax_id->if_version);
+ return TRUE;
+}
- return 8;
+p_cont_elem_t* rts_context_elem_new(size_t count)
+{
+ p_cont_elem_t* ctx = calloc(count, sizeof(p_cont_elem_t));
+ return ctx;
}
-static int rts_empty_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+void rts_context_elem_free(p_cont_elem_t* ptr)
{
- return 0;
+ if (!ptr)
+ return;
+ rts_syntax_id_free(ptr->transfer_syntaxes);
+ free(ptr);
}
-static int rts_empty_command_write(BYTE* buffer)
+static BOOL rts_read_context_elem(wStream* s, p_cont_elem_t* element)
{
- if (buffer)
+ BYTE x;
+ assert(s);
+ assert(element);
+
+ if (Stream_GetRemainingLength(s) < 4)
+ return FALSE;
+
+ Stream_Read_UINT16(s, element->p_cont_id);
+ Stream_Read_UINT8(s, element->n_transfer_syn); /* number of items */
+ Stream_Read_UINT8(s, element->reserved); /* alignment pad, m.b.z. */
+
+ if (!rts_read_syntax_id(s, &element->abstract_syntax)) /* transfer syntax list */
+ return FALSE;
+
+ if (element->n_transfer_syn > 0)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_EMPTY; /* CommandType (4 bytes) */
+ element->transfer_syntaxes = rts_syntax_id_new(element->n_transfer_syn);
+ if (!element->transfer_syntaxes)
+ return FALSE;
+ for (x = 0; x < element->n_transfer_syn; x++)
+ {
+ p_syntax_id_t* syn = &element->transfer_syntaxes[x];
+ if (!rts_read_syntax_id(s, syn)) /* size_is(n_transfer_syn) */
+ return FALSE;
+ }
}
- return 4;
+ return TRUE;
}
-static SSIZE_T rts_padding_command_read(const BYTE* buffer, size_t length)
+static BOOL rts_write_context_elem(wStream* s, const p_cont_elem_t* element)
{
- UINT32 ConformanceCount;
- ConformanceCount = *((UINT32*)&buffer[0]); /* ConformanceCount (4 bytes) */
- /* Padding (variable) */
- return ConformanceCount + 4;
+ BYTE x;
+ assert(s);
+ assert(element);
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return FALSE;
+ Stream_Write_UINT16(s, element->p_cont_id);
+ Stream_Write_UINT8(s, element->n_transfer_syn); /* number of items */
+ Stream_Write_UINT8(s, element->reserved); /* alignment pad, m.b.z. */
+ if (!rts_write_syntax_id(s, &element->abstract_syntax)) /* transfer syntax list */
+ return FALSE;
+
+ for (x = 0; x < element->n_transfer_syn; x++)
+ {
+ const p_syntax_id_t* syn = &element->transfer_syntaxes[x];
+ if (!rts_write_syntax_id(s, syn)) /* size_is(n_transfer_syn) */
+ return FALSE;
+ }
+
+ return TRUE;
}
-static int rts_padding_command_write(BYTE* buffer, UINT32 ConformanceCount)
+static BOOL rts_read_context_list(wStream* s, p_cont_list_t* list)
{
- if (buffer)
+ BYTE x;
+
+ assert(s);
+ assert(list);
+
+ if (Stream_GetRemainingLength(s) < 4)
+ return FALSE;
+ Stream_Read_UINT8(s, list->n_context_elem); /* number of items */
+ Stream_Read_UINT8(s, list->reserved); /* alignment pad, m.b.z. */
+ Stream_Read_UINT16(s, list->reserved2); /* alignment pad, m.b.z. */
+
+ if (list->n_context_elem > 0)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_PADDING; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = ConformanceCount; /* ConformanceCount (4 bytes) */
- ZeroMemory(&buffer[8], ConformanceCount); /* Padding (variable) */
+ list->p_cont_elem = rts_context_elem_new(list->n_context_elem);
+ if (!list->p_cont_elem)
+ return FALSE;
+ for (x = 0; x < list->n_context_elem; x++)
+ {
+ p_cont_elem_t* element = &list->p_cont_elem[x];
+ if (!rts_read_context_elem(s, element))
+ return FALSE;
+ }
}
-
- return 8 + ConformanceCount;
+ return TRUE;
}
-static int rts_negative_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static void rts_free_context_list(p_cont_list_t* list)
{
- return 0;
+ if (!list)
+ return;
+ rts_context_elem_free(list->p_cont_elem);
}
-static int rts_negative_ance_command_write(BYTE* buffer)
+static BOOL rts_write_context_list(wStream* s, const p_cont_list_t* list)
{
- if (buffer)
+ BYTE x;
+
+ assert(s);
+ assert(list);
+
+ if (!Stream_EnsureRemainingCapacity(s, 4))
+ return FALSE;
+ Stream_Write_UINT8(s, list->n_context_elem); /* number of items */
+ Stream_Write_UINT8(s, 0); /* alignment pad, m.b.z. */
+ Stream_Write_UINT16(s, 0); /* alignment pad, m.b.z. */
+
+ for (x = 0; x < list->n_context_elem; x++)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_NEGATIVE_ANCE; /* CommandType (4 bytes) */
+ const p_cont_elem_t* element = &list->p_cont_elem[x];
+ if (!rts_write_context_elem(s, element))
+ return FALSE;
}
+ return TRUE;
+}
- return 4;
+static p_result_t* rts_result_new(size_t count)
+{
+ return calloc(count, sizeof(p_result_t));
}
-static int rts_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static void rts_result_free(p_result_t* results)
{
- return 0;
+ if (!results)
+ return;
+ free(results);
}
-static int rts_ance_command_write(BYTE* buffer)
+static BOOL rts_read_result(wStream* s, p_result_t* result)
{
- if (buffer)
- {
- *((UINT32*)&buffer[0]) = RTS_CMD_ANCE; /* CommandType (4 bytes) */
- }
+ assert(s);
+ assert(result);
- return 4;
+ if (Stream_GetRemainingLength(s) < 2)
+ return FALSE;
+ Stream_Read_UINT16(s, result->result);
+ Stream_Read_UINT16(s, result->reason);
+
+ return rts_read_syntax_id(s, &result->transfer_syntax);
}
-static SSIZE_T rts_client_address_command_read(const BYTE* buffer, size_t length)
+static void rts_free_result(p_result_t* result)
{
- UINT32 AddressType;
- AddressType = *((UINT32*)&buffer[0]); /* AddressType (4 bytes) */
+ if (!result)
+ return;
+}
- if (AddressType == 0)
+static BOOL rts_read_result_list(wStream* s, p_result_list_t* list)
+{
+ BYTE x;
+
+ assert(s);
+ assert(list);
+
+ if (Stream_GetRemainingLength(s) < 4)
+ return FALSE;
+ Stream_Read_UINT8(s, list->n_results); /* count */
+ Stream_Read_UINT8(s, list->reserved); /* alignment pad, m.b.z. */
+ Stream_Read_UINT16(s, list->reserved2); /* alignment pad, m.b.z. */
+
+ if (list->n_results > 0)
{
- /* ClientAddress (4 bytes) */
- /* padding (12 bytes) */
- return 4 + 4 + 12;
+ list->p_results = rts_result_new(list->n_results);
+ if (!list->p_results)
+ return FALSE;
+
+ for (x = 0; x < list->n_results; x++)
+ {
+ p_result_t* result = &list->p_results[x]; /* size_is(n_results) */
+ if (!rts_read_result(s, result))
+ return FALSE;
+ }
}
- else
+
+ return TRUE;
+}
+
+static void rts_free_result_list(p_result_list_t* list)
+{
+ BYTE x;
+
+ if (!list)
+ return;
+ for (x = 0; x < list->n_results; x++)
{
- /* ClientAddress (16 bytes) */
- /* padding (12 bytes) */
- return 4 + 16 + 12;
+ p_result_t* result = &list->p_results[x];
+ rts_free_result(result);
}
+ rts_result_free(list->p_results);
+}
+
+static void rts_free_pdu_alter_context(rpcconn_alter_context_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+
+ rts_free_context_list(&ctx->p_context_elem);
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_alter_context(wStream* s, rpcconn_alter_context_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_alter_context_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+
+ Stream_Read_UINT16(s, ctx->max_xmit_frag);
+ Stream_Read_UINT16(s, ctx->max_recv_frag);
+ Stream_Read_UINT32(s, ctx->assoc_group_id);
+
+ if (!rts_read_context_list(s, &ctx->p_context_elem))
+ return FALSE;
+
+ if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header))
+ return FALSE;
+
+ return TRUE;
}
-static int rts_client_address_command_write(BYTE* buffer, UINT32 AddressType, BYTE* ClientAddress)
+static BOOL rts_read_pdu_alter_context_response(wStream* s,
+ rpcconn_alter_context_response_hdr_t* ctx)
{
- if (buffer)
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_alter_context_response_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT16(s, ctx->max_xmit_frag);
+ Stream_Read_UINT16(s, ctx->max_recv_frag);
+ Stream_Read_UINT32(s, ctx->assoc_group_id);
+
+ if (!rts_read_port_any(s, &ctx->sec_addr))
+ return FALSE;
+
+ if (!rts_align_stream(s, 4))
+ return FALSE;
+
+ if (!rts_read_result_list(s, &ctx->p_result_list))
+ return FALSE;
+
+ if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void rts_free_pdu_alter_context_response(rpcconn_alter_context_response_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+
+ rts_free_port_any(&ctx->sec_addr);
+ rts_free_result_list(&ctx->p_result_list);
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_bind(wStream* s, rpcconn_bind_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_bind_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT16(s, ctx->max_xmit_frag);
+ Stream_Read_UINT16(s, ctx->max_recv_frag);
+ Stream_Read_UINT32(s, ctx->assoc_group_id);
+
+ if (!rts_read_context_list(s, &ctx->p_context_elem))
+ return FALSE;
+
+ if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void rts_free_pdu_bind(rpcconn_bind_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_context_list(&ctx->p_context_elem);
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_bind_ack(wStream* s, rpcconn_bind_ack_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_bind_ack_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT16(s, ctx->max_xmit_frag);
+ Stream_Read_UINT16(s, ctx->max_recv_frag);
+ Stream_Read_UINT32(s, ctx->assoc_group_id);
+
+ if (!rts_read_port_any(s, &ctx->sec_addr))
+ return FALSE;
+
+ if (!rts_align_stream(s, 4))
+ return FALSE;
+
+ if (!rts_read_result_list(s, &ctx->p_result_list))
+ return FALSE;
+
+ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_bind_ack(rpcconn_bind_ack_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_port_any(&ctx->sec_addr);
+ rts_free_result_list(&ctx->p_result_list);
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_bind_nak(wStream* s, rpcconn_bind_nak_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_bind_nak_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT16(s, ctx->provider_reject_reason);
+ return rts_read_supported_versions(s, &ctx->versions);
+}
+
+static void rts_free_pdu_bind_nak(rpcconn_bind_nak_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+
+ rts_free_supported_versions(&ctx->versions);
+}
+
+static BOOL rts_read_pdu_auth3(wStream* s, rpcconn_rpc_auth_3_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_rpc_auth_3_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT16(s, ctx->max_xmit_frag);
+ Stream_Read_UINT16(s, ctx->max_recv_frag);
+
+ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_auth3(rpcconn_rpc_auth_3_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_fault(wStream* s, rpcconn_fault_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_fault_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT32(s, ctx->alloc_hint);
+ Stream_Read_UINT16(s, ctx->p_cont_id);
+ Stream_Read_UINT8(s, ctx->cancel_count);
+ Stream_Read_UINT8(s, ctx->reserved);
+ Stream_Read_UINT32(s, ctx->status);
+
+ return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_fault(rpcconn_fault_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_cancel_ack(wStream* s, rpcconn_cancel_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_cancel_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_cancel_ack(rpcconn_cancel_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_orphaned(wStream* s, rpcconn_orphaned_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_orphaned_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_orphaned(rpcconn_orphaned_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_request(wStream* s, rpcconn_request_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_request_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT32(s, ctx->alloc_hint);
+ Stream_Read_UINT16(s, ctx->p_cont_id);
+ Stream_Read_UINT16(s, ctx->opnum);
+ if (!rts_read_uuid(s, &ctx->object))
+ return FALSE;
+
+ return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_request(rpcconn_request_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_response(wStream* s, rpcconn_response_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) <
+ sizeof(rpcconn_response_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+ Stream_Read_UINT32(s, ctx->alloc_hint);
+ Stream_Read_UINT16(s, ctx->p_cont_id);
+ Stream_Read_UINT8(s, ctx->cancel_count);
+ Stream_Read_UINT8(s, ctx->reserved);
+
+ if (!rts_align_stream(s, 8))
+ return FALSE;
+
+ return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header);
+}
+
+static void rts_free_pdu_response(rpcconn_response_hdr_t* ctx)
+{
+ if (!ctx)
+ return;
+ free(ctx->stub_data);
+ rts_free_auth_verifier(&ctx->auth_verifier);
+}
+
+static BOOL rts_read_pdu_rts(wStream* s, rpcconn_rts_hdr_t* ctx)
+{
+ assert(s);
+ assert(ctx);
+
+ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_rts_hdr_t) - sizeof(rpcconn_common_hdr_t))
+ return FALSE;
+
+ Stream_Read_UINT16(s, ctx->Flags);
+ Stream_Read_UINT16(s, ctx->NumberOfCommands);
+ return TRUE;
+}
+
+static void rts_free_pdu_rts(rpcconn_rts_hdr_t* ctx)
+{
+ WINPR_UNUSED(ctx);
+}
+
+void rts_free_pdu_header(rpcconn_hdr_t* header, BOOL allocated)
+{
+ if (!header)
+ return;
+
+ switch (header->common.ptype)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_CLIENT_ADDRESS; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = AddressType; /* AddressType (4 bytes) */
+ case PTYPE_ALTER_CONTEXT:
+ rts_free_pdu_alter_context(&header->alter_context);
+ break;
+ case PTYPE_ALTER_CONTEXT_RESP:
+ rts_free_pdu_alter_context_response(&header->alter_context_response);
+ break;
+ case PTYPE_BIND:
+ rts_free_pdu_bind(&header->bind);
+ break;
+ case PTYPE_BIND_ACK:
+ rts_free_pdu_bind_ack(&header->bind_ack);
+ break;
+ case PTYPE_BIND_NAK:
+ rts_free_pdu_bind_nak(&header->bind_nak);
+ break;
+ case PTYPE_RPC_AUTH_3:
+ rts_free_pdu_auth3(&header->rpc_auth_3);
+ break;
+ case PTYPE_CANCEL_ACK:
+ rts_free_pdu_cancel_ack(&header->cancel);
+ break;
+ case PTYPE_FAULT:
+ rts_free_pdu_fault(&header->fault);
+ break;
+ case PTYPE_ORPHANED:
+ rts_free_pdu_orphaned(&header->orphaned);
+ break;
+ case PTYPE_REQUEST:
+ rts_free_pdu_request(&header->request);
+ break;
+ case PTYPE_RESPONSE:
+ rts_free_pdu_response(&header->response);
+ break;
+ case PTYPE_RTS:
+ rts_free_pdu_rts(&header->rts);
+ break;
+ /* No extra fields */
+ case PTYPE_SHUTDOWN:
+ break;
+
+ /* not handled */
+ case PTYPE_PING:
+ case PTYPE_WORKING:
+ case PTYPE_NOCALL:
+ case PTYPE_REJECT:
+ case PTYPE_ACK:
+ case PTYPE_CL_CANCEL:
+ case PTYPE_FACK:
+ case PTYPE_CO_CANCEL:
+ default:
+ break;
}
- if (AddressType == 0)
+ if (allocated)
+ free(header);
+}
+
+BOOL rts_read_pdu_header(wStream* s, rpcconn_hdr_t* header)
+{
+ BOOL rc = FALSE;
+ assert(s);
+ assert(header);
+
+ if (!rts_read_common_pdu_header(s, &header->common))
+ return FALSE;
+
+ WLog_DBG(TAG, "Reading PDU type %s", rts_pdu_ptype_to_string(header->common.ptype));
+ fflush(stdout);
+ switch (header->common.ptype)
{
- if (buffer)
- {
- CopyMemory(&buffer[8], ClientAddress, 4); /* ClientAddress (4 bytes) */
- ZeroMemory(&buffer[12], 12); /* padding (12 bytes) */
- }
+ case PTYPE_ALTER_CONTEXT:
+ rc = rts_read_pdu_alter_context(s, &header->alter_context);
+ break;
+ case PTYPE_ALTER_CONTEXT_RESP:
+ rc = rts_read_pdu_alter_context_response(s, &header->alter_context_response);
+ break;
+ case PTYPE_BIND:
+ rc = rts_read_pdu_bind(s, &header->bind);
+ break;
+ case PTYPE_BIND_ACK:
+ rc = rts_read_pdu_bind_ack(s, &header->bind_ack);
+ break;
+ case PTYPE_BIND_NAK:
+ rc = rts_read_pdu_bind_nak(s, &header->bind_nak);
+ break;
+ case PTYPE_RPC_AUTH_3:
+ rc = rts_read_pdu_auth3(s, &header->rpc_auth_3);
+ break;
+ case PTYPE_CANCEL_ACK:
+ rc = rts_read_pdu_cancel_ack(s, &header->cancel);
+ break;
+ case PTYPE_FAULT:
+ rc = rts_read_pdu_fault(s, &header->fault);
+ break;
+ case PTYPE_ORPHANED:
+ rc = rts_read_pdu_orphaned(s, &header->orphaned);
+ break;
+ case PTYPE_REQUEST:
+ rc = rts_read_pdu_request(s, &header->request);
+ break;
+ case PTYPE_RESPONSE:
+ rc = rts_read_pdu_response(s, &header->response);
+ break;
+ case PTYPE_RTS:
+ rc = rts_read_pdu_rts(s, &header->rts);
+ break;
+ case PTYPE_SHUTDOWN:
+ rc = TRUE; /* No extra fields */
+ break;
- return 24;
+ /* not handled */
+ case PTYPE_PING:
+ case PTYPE_WORKING:
+ case PTYPE_NOCALL:
+ case PTYPE_REJECT:
+ case PTYPE_ACK:
+ case PTYPE_CL_CANCEL:
+ case PTYPE_FACK:
+ case PTYPE_CO_CANCEL:
+ default:
+ break;
}
+
+ return rc;
+}
+
+static BOOL rts_write_pdu_header(wStream* s, const rpcconn_rts_hdr_t* header)
+{
+ assert(s);
+ assert(header);
+ if (!Stream_EnsureRemainingCapacity(s, sizeof(rpcconn_rts_hdr_t)))
+ return FALSE;
+
+ if (!rts_write_common_pdu_header(s, &header->header))
+ return FALSE;
+
+ Stream_Write_UINT16(s, header->Flags);
+ Stream_Write_UINT16(s, header->NumberOfCommands);
+ return TRUE;
+}
+
+static int rts_receive_window_size_command_read(rdpRpc* rpc, wStream* buffer,
+ UINT32* ReceiveWindowSize)
+{
+ UINT32 val;
+
+ assert(rpc);
+ assert(buffer);
+
+ if (Stream_GetRemainingLength(buffer) < 4)
+ return -1;
+ Stream_Read_UINT32(buffer, val);
+ if (ReceiveWindowSize)
+ *ReceiveWindowSize = val; /* ReceiveWindowSize (4 bytes) */
+
+ return 4;
+}
+
+static BOOL rts_receive_window_size_command_write(wStream* s, UINT32 ReceiveWindowSize)
+{
+ assert(s);
+
+ if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT32)))
+ return FALSE;
+
+ Stream_Write_UINT32(s, RTS_CMD_RECEIVE_WINDOW_SIZE); /* CommandType (4 bytes) */
+ Stream_Write_UINT32(s, ReceiveWindowSize); /* ReceiveWindowSize (4 bytes) */
+
+ return TRUE;
+}
+
+static int rts_flow_control_ack_command_read(rdpRpc* rpc, wStream* buffer, UINT32* BytesReceived,
+ UINT32* AvailableWindow, BYTE* ChannelCookie)
+{
+ UINT32 val;
+ assert(rpc);
+ assert(buffer);
+
+ /* Ack (24 bytes) */
+ if (Stream_GetRemainingLength(buffer) < 24)
+ return -1;
+
+ Stream_Read_UINT32(buffer, val);
+ if (BytesReceived)
+ *BytesReceived = val; /* BytesReceived (4 bytes) */
+
+ Stream_Read_UINT32(buffer, val);
+ if (AvailableWindow)
+ *AvailableWindow = val; /* AvailableWindow (4 bytes) */
+
+ if (ChannelCookie)
+ Stream_Read(buffer, ChannelCookie, 16); /* ChannelCookie (16 bytes) */
else
- {
- if (buffer)
- {
- CopyMemory(&buffer[8], ClientAddress, 16); /* ClientAddress (16 bytes) */
- ZeroMemory(&buffer[24], 12); /* padding (12 bytes) */
- }
+ Stream_Seek(buffer, 16);
+ return 24;
+}
- return 36;
- }
+static BOOL rts_flow_control_ack_command_write(wStream* s, UINT32 BytesReceived,
+ UINT32 AvailableWindow, BYTE* ChannelCookie)
+{
+ assert(s);
+
+ if (!Stream_EnsureRemainingCapacity(s, 28))
+ return FALSE;
+
+ Stream_Write_UINT32(s, RTS_CMD_FLOW_CONTROL_ACK); /* CommandType (4 bytes) */
+ Stream_Write_UINT32(s, BytesReceived); /* BytesReceived (4 bytes) */
+ Stream_Write_UINT32(s, AvailableWindow); /* AvailableWindow (4 bytes) */
+ Stream_Write(s, ChannelCookie, 16); /* ChannelCookie (16 bytes) */
+
+ return TRUE;
}
-static int rts_association_group_id_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static BOOL rts_connection_timeout_command_read(rdpRpc* rpc, wStream* buffer,
+ UINT32* ConnectionTimeout)
{
- /* AssociationGroupId (16 bytes) */
- return 16;
+ UINT32 val;
+ assert(rpc);
+ assert(buffer);
+
+ if (Stream_GetRemainingLength(buffer) < 4)
+ return FALSE;
+
+ Stream_Read_UINT32(buffer, val);
+ if (ConnectionTimeout)
+ *ConnectionTimeout = val; /* ConnectionTimeout (4 bytes) */
+
+ return TRUE;
}
-static int rts_association_group_id_command_write(BYTE* buffer, BYTE* AssociationGroupId)
+static BOOL rts_cookie_command_write(wStream* s, const BYTE* Cookie)
{
- if (buffer)
- {
- *((UINT32*)&buffer[0]) = RTS_CMD_ASSOCIATION_GROUP_ID; /* CommandType (4 bytes) */
- CopyMemory(&buffer[4], AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */
- }
+ assert(s);
+
+ if (!Stream_EnsureRemainingCapacity(s, 20))
+ return FALSE;
- return 20;
+ Stream_Write_UINT32(s, RTS_CMD_COOKIE); /* CommandType (4 bytes) */
+ Stream_Write(s, Cookie, 16); /* Cookie (16 bytes) */
+
+ return TRUE;
}
-static int rts_destination_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length,
- UINT32* Destination)
+static BOOL rts_channel_lifetime_command_write(wStream* s, UINT32 ChannelLifetime)
{
- if (Destination)
- *Destination = *((UINT32*)&buffer[0]); /* Destination (4 bytes) */
+ assert(s);
- return 4;
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+ Stream_Write_UINT32(s, RTS_CMD_CHANNEL_LIFETIME); /* CommandType (4 bytes) */
+ Stream_Write_UINT32(s, ChannelLifetime); /* ChannelLifetime (4 bytes) */
+
+ return TRUE;
+}
+
+static BOOL rts_client_keepalive_command_write(wStream* s, UINT32 ClientKeepalive)
+{
+ assert(s);
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+ /**
+ * An unsigned integer that specifies the keep-alive interval, in milliseconds,
+ * that this connection is configured to use. This value MUST be 0 or in the inclusive
+ * range of 60,000 through 4,294,967,295. If it is 0, it MUST be interpreted as 300,000.
+ */
+
+ Stream_Write_UINT32(s, RTS_CMD_CLIENT_KEEPALIVE); /* CommandType (4 bytes) */
+ Stream_Write_UINT32(s, ClientKeepalive); /* ClientKeepalive (4 bytes) */
+
+ return TRUE;
+}
+
+static BOOL rts_version_command_read(rdpRpc* rpc, wStream* buffer)
+{
+ assert(rpc);
+ assert(buffer);
+
+ if (!Stream_SafeSeek(buffer, 4))
+ return FALSE;
+
+ /* Version (4 bytes) */
+ return TRUE;
+}
+
+static BOOL rts_version_command_write(wStream* buffer)
+{
+ assert(buffer);
+
+ if (Stream_GetRemainingCapacity(buffer) < 8)
+ return FALSE;
+
+ Stream_Write_UINT32(buffer, RTS_CMD_VERSION); /* CommandType (4 bytes) */
+ Stream_Write_UINT32(buffer, 1); /* Version (4 bytes) */
+
+ return TRUE;
}
-static int rts_destination_command_write(BYTE* buffer, UINT32 Destination)
+static BOOL rts_empty_command_write(wStream* s)
{
- if (buffer)
+ assert(s);
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT32(s, RTS_CMD_EMPTY); /* CommandType (4 bytes) */
+
+ return TRUE;
+}
+
+static BOOL rts_padding_command_read(wStream* s, size_t* length)
+{
+ UINT32 ConformanceCount;
+ assert(s);
+ assert(length);
+ if (Stream_GetRemainingLength(s) < 4)
+ return FALSE;
+ Stream_Read_UINT32(s, ConformanceCount); /* ConformanceCount (4 bytes) */
+ *length = ConformanceCount + 4;
+ return TRUE;
+}
+
+static BOOL rts_client_address_command_read(wStream* s, size_t* length)
+{
+ UINT32 AddressType;
+
+ assert(s);
+ assert(length);
+
+ if (Stream_GetRemainingLength(s) < 4)
+ return FALSE;
+ Stream_Read_UINT32(s, AddressType); /* AddressType (4 bytes) */
+
+ if (AddressType == 0)
{
- *((UINT32*)&buffer[0]) = RTS_CMD_DESTINATION; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = Destination; /* Destination (4 bytes) */
+ /* ClientAddress (4 bytes) */
+ /* padding (12 bytes) */
+ *length = 4 + 4 + 12;
}
+ else
+ {
+ /* ClientAddress (16 bytes) */
+ /* padding (12 bytes) */
+ *length = 4 + 16 + 12;
+ }
+ return TRUE;
+}
+
+static BOOL rts_association_group_id_command_write(wStream* s, const BYTE* AssociationGroupId)
+{
+ assert(s);
- return 8;
+ if (!Stream_EnsureRemainingCapacity(s, 20))
+ return FALSE;
+
+ Stream_Write_UINT32(s, RTS_CMD_ASSOCIATION_GROUP_ID); /* CommandType (4 bytes) */
+ Stream_Write(s, AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */
+
+ return TRUE;
}
-static int rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static int rts_destination_command_read(rdpRpc* rpc, wStream* buffer, UINT32* Destination)
{
- /* PingTrafficSent (4 bytes) */
+ UINT32 val;
+ assert(rpc);
+ assert(buffer);
+
+ if (Stream_GetRemainingLength(buffer) < 4)
+ return -1;
+ Stream_Read_UINT32(buffer, val);
+ if (Destination)
+ *Destination = val; /* Destination (4 bytes) */
+
return 4;
}
-static int rts_ping_traffic_sent_notify_command_write(BYTE* buffer, UINT32 PingTrafficSent)
+static BOOL rts_destination_command_write(wStream* s, UINT32 Destination)
{
- if (buffer)
- {
- *((UINT32*)&buffer[0]) = RTS_CMD_PING_TRAFFIC_SENT_NOTIFY; /* CommandType (4 bytes) */
- *((UINT32*)&buffer[4]) = PingTrafficSent; /* PingTrafficSent (4 bytes) */
- }
+ assert(s);
- return 8;
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT32(s, RTS_CMD_DESTINATION); /* CommandType (4 bytes) */
+ Stream_Write_UINT32(s, Destination); /* Destination (4 bytes) */
+
+ return TRUE;
}
void rts_generate_cookie(BYTE* cookie)
{
+ assert(cookie);
winpr_RAND(cookie, 16);
}
+static BOOL rts_send_buffer(RpcChannel* channel, wStream* s, size_t frag_length)
+{
+ BOOL status = FALSE;
+ SSIZE_T rc;
+
+ assert(channel);
+ assert(s);
+
+ Stream_SealLength(s);
+ if (Stream_Length(s) < sizeof(rpcconn_common_hdr_t))
+ goto fail;
+ if (Stream_Length(s) != frag_length)
+ goto fail;
+
+ rc = rpc_channel_write(channel, Stream_Buffer(s), Stream_Length(s));
+ if (rc < 0)
+ goto fail;
+ if ((size_t)rc != Stream_Length(s))
+ goto fail;
+ status = TRUE;
+fail:
+ return status;
+}
+
/* CONN/A Sequence */
-int rts_send_CONN_A1_pdu(rdpRpc* rpc)
+BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
UINT32 ReceiveWindowSize;
BYTE* OUTChannelCookie;
BYTE* VirtualConnectionCookie;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
- RpcOutChannel* outChannel = connection->DefaultOutChannel;
+ RpcVirtualConnection* connection;
+ RpcOutChannel* outChannel;
+
+ assert(rpc);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
+ outChannel = connection->DefaultOutChannel;
+ assert(outChannel);
header.header.frag_length = 76;
header.Flags = RTS_FLAG_NONE;
header.NumberOfCommands = 4;
+
WLog_DBG(TAG, "Sending CONN/A1 RTS PDU");
VirtualConnectionCookie = (BYTE*)&(connection->Cookie);
OUTChannelCookie = (BYTE*)&(outChannel->common.Cookie);
ReceiveWindowSize = outChannel->ReceiveWindow;
- buffer = (BYTE*)malloc(header.header.frag_length);
+
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
return -1;
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_version_command_write(&buffer[20]); /* Version (8 bytes) */
- rts_cookie_command_write(&buffer[28],
- VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
- rts_cookie_command_write(&buffer[48], OUTChannelCookie); /* OUTChannelCookie (20 bytes) */
- rts_receive_window_size_command_write(&buffer[68],
- ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
- status = rpc_channel_write(&outChannel->common, buffer, header.header.frag_length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+ status = rts_version_command_write(buffer); /* Version (8 bytes) */
+ if (!status)
+ goto fail;
+ status = rts_cookie_command_write(
+ buffer, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
+ if (!status)
+ goto fail;
+ status = rts_cookie_command_write(buffer, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */
+ if (!status)
+ goto fail;
+ status = rts_receive_window_size_command_write(
+ buffer, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
+ if (!status)
+ goto fail;
+ status = rts_send_buffer(&outChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return status;
}
-int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer)
{
+ BOOL rc;
UINT32 ConnectionTimeout;
- rts_connection_timeout_command_read(rpc, &buffer[24], length - 24, &ConnectionTimeout);
+
+ if (!Stream_SafeSeek(buffer, 24))
+ return FALSE;
+
+ rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout);
+ if (!rc)
+ return rc;
+
WLog_DBG(TAG, "Receiving CONN/A3 RTS PDU: ConnectionTimeout: %" PRIu32 "", ConnectionTimeout);
+
+ assert(rpc);
+ assert(rpc->VirtualConnection);
+ assert(rpc->VirtualConnection->DefaultInChannel);
+
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
- return 1;
+ return TRUE;
}
/* CONN/B Sequence */
-int rts_send_CONN_B1_pdu(rdpRpc* rpc)
+BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
- UINT32 length;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
BYTE* INChannelCookie;
BYTE* AssociationGroupId;
BYTE* VirtualConnectionCookie;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
- RpcInChannel* inChannel = connection->DefaultInChannel;
+ RpcVirtualConnection* connection;
+ RpcInChannel* inChannel;
+
+ assert(rpc);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
+ inChannel = connection->DefaultInChannel;
+ assert(inChannel);
header.header.frag_length = 104;
header.Flags = RTS_FLAG_NONE;
header.NumberOfCommands = 6;
+
WLog_DBG(TAG, "Sending CONN/B1 RTS PDU");
+
VirtualConnectionCookie = (BYTE*)&(connection->Cookie);
INChannelCookie = (BYTE*)&(inChannel->common.Cookie);
AssociationGroupId = (BYTE*)&(connection->AssociationGroupId);
- buffer = (BYTE*)malloc(header.header.frag_length);
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
- return -1;
-
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_version_command_write(&buffer[20]); /* Version (8 bytes) */
- rts_cookie_command_write(&buffer[28],
- VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
- rts_cookie_command_write(&buffer[48], INChannelCookie); /* INChannelCookie (20 bytes) */
- rts_channel_lifetime_command_write(&buffer[68],
- rpc->ChannelLifetime); /* ChannelLifetime (8 bytes) */
- rts_client_keepalive_command_write(&buffer[76],
- rpc->KeepAliveInterval); /* ClientKeepalive (8 bytes) */
- rts_association_group_id_command_write(&buffer[84],
- AssociationGroupId); /* AssociationGroupId (20 bytes) */
- length = header.header.frag_length;
- status = rpc_channel_write(&inChannel->common, buffer, length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ goto fail;
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+ if (!rts_version_command_write(buffer)) /* Version (8 bytes) */
+ goto fail;
+ if (!rts_cookie_command_write(buffer,
+ VirtualConnectionCookie)) /* VirtualConnectionCookie (20 bytes) */
+ goto fail;
+ if (!rts_cookie_command_write(buffer, INChannelCookie)) /* INChannelCookie (20 bytes) */
+ goto fail;
+ if (!rts_channel_lifetime_command_write(buffer,
+ rpc->ChannelLifetime)) /* ChannelLifetime (8 bytes) */
+ goto fail;
+ if (!rts_client_keepalive_command_write(buffer,
+ rpc->KeepAliveInterval)) /* ClientKeepalive (8 bytes) */
+ goto fail;
+ if (!rts_association_group_id_command_write(
+ buffer, AssociationGroupId)) /* AssociationGroupId (20 bytes) */
+ goto fail;
+ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return status;
}
/* CONN/C Sequence */
-int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer)
{
- UINT32 offset;
+ BOOL rc;
UINT32 ReceiveWindowSize;
UINT32 ConnectionTimeout;
- offset = 24;
- offset += rts_version_command_read(rpc, &buffer[offset], length - offset) + 4;
- offset += rts_receive_window_size_command_read(rpc, &buffer[offset], length - offset,
- &ReceiveWindowSize) +
- 4;
- offset += rts_connection_timeout_command_read(rpc, &buffer[offset], length - offset,
- &ConnectionTimeout) +
- 4;
+
+ assert(rpc);
+ assert(buffer);
+
+ if (!Stream_SafeSeek(buffer, 24))
+ return FALSE;
+
+ rc = rts_version_command_read(rpc, buffer);
+ if (rc < 0)
+ return rc;
+ rc = rts_receive_window_size_command_read(rpc, buffer, &ReceiveWindowSize);
+ if (rc < 0)
+ return rc;
+ rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout);
+ if (rc < 0)
+ return rc;
WLog_DBG(TAG,
"Receiving CONN/C2 RTS PDU: ConnectionTimeout: %" PRIu32 " ReceiveWindowSize: %" PRIu32
"",
ConnectionTimeout, ReceiveWindowSize);
+
+ assert(rpc);
+ assert(rpc->VirtualConnection);
+ assert(rpc->VirtualConnection->DefaultInChannel);
+
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = ReceiveWindowSize;
- return 1;
+ return TRUE;
}
/* Out-of-Sequence PDUs */
-static int rts_send_keep_alive_pdu(rdpRpc* rpc)
+BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
- UINT32 length;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
- RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel;
+ UINT32 BytesReceived;
+ UINT32 AvailableWindow;
+ BYTE* ChannelCookie;
+ RpcVirtualConnection* connection;
+ RpcInChannel* inChannel;
+ RpcOutChannel* outChannel;
- header.header.frag_length = 28;
- header.Flags = RTS_FLAG_OTHER_CMD;
- header.NumberOfCommands = 1;
- WLog_DBG(TAG, "Sending Keep-Alive RTS PDU");
- buffer = (BYTE*)malloc(header.header.frag_length);
+ assert(rpc);
- if (!buffer)
- return -1;
+ connection = rpc->VirtualConnection;
+ assert(connection);
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_client_keepalive_command_write(
- &buffer[20], rpc->CurrentKeepAliveInterval); /* ClientKeepAlive (8 bytes) */
- length = header.header.frag_length;
- status = rpc_channel_write(&inChannel->common, buffer, length);
- free(buffer);
- return (status > 0) ? 1 : -1;
-}
+ inChannel = connection->DefaultInChannel;
+ assert(inChannel);
-int rts_send_flow_control_ack_pdu(rdpRpc* rpc)
-{
- int status;
- BYTE* buffer;
- UINT32 length;
- rpcconn_rts_hdr_t header = rts_pdu_header_init();
- UINT32 BytesReceived;
- UINT32 AvailableWindow;
- BYTE* ChannelCookie;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
- RpcInChannel* inChannel = connection->DefaultInChannel;
- RpcOutChannel* outChannel = connection->DefaultOutChannel;
+ outChannel = connection->DefaultOutChannel;
+ assert(outChannel);
header.header.frag_length = 56;
header.Flags = RTS_FLAG_OTHER_CMD;
header.NumberOfCommands = 2;
+
WLog_DBG(TAG, "Sending FlowControlAck RTS PDU");
+
BytesReceived = outChannel->BytesReceived;
AvailableWindow = outChannel->AvailableWindowAdvertised;
ChannelCookie = (BYTE*)&(outChannel->common.Cookie);
outChannel->ReceiverAvailableWindow = outChannel->AvailableWindowAdvertised;
- buffer = (BYTE*)malloc(header.header.frag_length);
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
- return -1;
+ goto fail;
+
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+ if (!rts_destination_command_write(buffer, FDOutProxy)) /* Destination Command (8 bytes) */
+ goto fail;
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_destination_command_write(&buffer[20], FDOutProxy); /* Destination Command (8 bytes) */
/* FlowControlAck Command (28 bytes) */
- rts_flow_control_ack_command_write(&buffer[28], BytesReceived, AvailableWindow, ChannelCookie);
- length = header.header.frag_length;
- status = rpc_channel_write(&inChannel->common, buffer, length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ if (!rts_flow_control_ack_command_write(buffer, BytesReceived, AvailableWindow, ChannelCookie))
+ goto fail;
+
+ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return status;
}
-static int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, wStream* buffer)
{
- UINT32 offset;
+ int rc;
UINT32 BytesReceived;
UINT32 AvailableWindow;
- BYTE ChannelCookie[16];
- offset = 24;
- offset +=
- rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset, &BytesReceived,
- &AvailableWindow, (BYTE*)&ChannelCookie) +
- 4;
+ BYTE ChannelCookie[16] = { 0 };
+
+ rc = rts_flow_control_ack_command_read(rpc, buffer, &BytesReceived, &AvailableWindow,
+ (BYTE*)&ChannelCookie);
+ if (rc < 0)
+ return rc;
WLog_ERR(TAG,
"Receiving FlowControlAck RTS PDU: BytesReceived: %" PRIu32
" AvailableWindow: %" PRIu32 "",
BytesReceived, AvailableWindow);
+
+ assert(rpc->VirtualConnection);
+ assert(rpc->VirtualConnection->DefaultInChannel);
+
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived);
return 1;
}
-static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, wStream* buffer)
{
- UINT32 offset;
+ int rc;
UINT32 Destination;
UINT32 BytesReceived;
UINT32 AvailableWindow;
- BYTE ChannelCookie[16];
+ BYTE ChannelCookie[16] = { 0 };
/**
* When the sender receives a FlowControlAck RTS PDU, it MUST use the following formula to
* recalculate its Sender AvailableWindow variable:
@@ -622,16 +1722,23 @@ static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buf
* structure in the PDU received.
*
*/
- offset = 24;
- offset += rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination) + 4;
- offset +=
- rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset, &BytesReceived,
- &AvailableWindow, (BYTE*)&ChannelCookie) +
- 4;
+
+ rc = rts_destination_command_read(rpc, buffer, &Destination);
+ if (rc < 0)
+ return rc;
+
+ rc = rts_flow_control_ack_command_read(rpc, buffer, &BytesReceived, &AvailableWindow,
+ ChannelCookie);
+ if (rc < 0)
+ return rc;
+
WLog_DBG(TAG,
"Receiving FlowControlAckWithDestination RTS PDU: BytesReceived: %" PRIu32
" AvailableWindow: %" PRIu32 "",
BytesReceived, AvailableWindow);
+
+ assert(rpc->VirtualConnection);
+ assert(rpc->VirtualConnection->DefaultInChannel);
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived);
return 1;
@@ -639,31 +1746,41 @@ static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buf
static int rts_send_ping_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
- UINT32 length;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
- RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel;
+ RpcInChannel* inChannel;
+
+ assert(rpc);
+ assert(rpc->VirtualConnection);
+
+ inChannel = rpc->VirtualConnection->DefaultInChannel;
+ assert(inChannel);
header.header.frag_length = 20;
header.Flags = RTS_FLAG_PING;
header.NumberOfCommands = 0;
+
WLog_DBG(TAG, "Sending Ping RTS PDU");
- buffer = (BYTE*)malloc(header.header.frag_length);
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
- return -1;
-
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- length = header.header.frag_length;
- status = rpc_channel_write(&inChannel->common, buffer, length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ goto fail;
+
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return (status) ? 1 : -1;
}
-SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length)
+BOOL rts_command_length(UINT32 CommandType, wStream* s, size_t* length)
{
- int CommandLength = 0;
+ size_t padding = 0;
+ size_t CommandLength = 0;
+
+ assert(s);
switch (CommandType)
{
@@ -700,7 +1817,8 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length
break;
case RTS_CMD_PADDING: /* variable-size */
- CommandLength = rts_padding_command_read(buffer, length);
+ if (!rts_padding_command_read(s, &padding))
+ return FALSE;
break;
case RTS_CMD_NEGATIVE_ANCE:
@@ -712,7 +1830,8 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length
break;
case RTS_CMD_CLIENT_ADDRESS: /* variable-size */
- CommandLength = rts_client_address_command_read(buffer, length);
+ if (!rts_client_address_command_read(s, &CommandLength))
+ return FALSE;
break;
case RTS_CMD_ASSOCIATION_GROUP_ID:
@@ -729,118 +1848,176 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length
default:
WLog_ERR(TAG, "Error: Unknown RTS Command Type: 0x%" PRIx32 "", CommandType);
- return -1;
+ return FALSE;
}
- return CommandLength;
+ CommandLength += padding;
+ if (Stream_GetRemainingLength(s) < CommandLength)
+ return FALSE;
+
+ if (length)
+ *length = CommandLength;
+ return TRUE;
}
static int rts_send_OUT_R2_A7_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
BYTE* SuccessorChannelCookie;
- RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel;
- RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
+ RpcInChannel* inChannel;
+ RpcOutChannel* nextOutChannel;
+
+ assert(rpc);
+ assert(rpc->VirtualConnection);
+
+ inChannel = rpc->VirtualConnection->DefaultInChannel;
+ assert(inChannel);
+
+ nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
+ assert(nextOutChannel);
header.header.frag_length = 56;
header.Flags = RTS_FLAG_OUT_CHANNEL;
header.NumberOfCommands = 3;
+
WLog_DBG(TAG, "Sending OUT_R2/A7 RTS PDU");
+
SuccessorChannelCookie = (BYTE*)&(nextOutChannel->common.Cookie);
- buffer = (BYTE*)malloc(header.header.frag_length);
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
return -1;
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_destination_command_write(&buffer[20], FDServer); /* Destination (8 bytes)*/
- rts_cookie_command_write(&buffer[28],
- SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */
- rts_version_command_write(&buffer[48]); /* Version (8 bytes) */
- status = rpc_channel_write(&inChannel->common, buffer, header.header.frag_length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+ if (!rts_destination_command_write(buffer, FDServer)) /* Destination (8 bytes)*/
+ goto fail;
+ if (!rts_cookie_command_write(buffer,
+ SuccessorChannelCookie)) /* SuccessorChannelCookie (20 bytes) */
+ goto fail;
+ if (!rts_version_command_write(buffer)) /* Version (8 bytes) */
+ goto fail;
+ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return (status) ? 1 : -1;
}
static int rts_send_OUT_R2_C1_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
- RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
+ RpcOutChannel* nextOutChannel;
+
+ assert(rpc);
+ assert(rpc->VirtualConnection);
+
+ nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
+ assert(nextOutChannel);
header.header.frag_length = 24;
header.Flags = RTS_FLAG_PING;
header.NumberOfCommands = 1;
+
WLog_DBG(TAG, "Sending OUT_R2/C1 RTS PDU");
- buffer = (BYTE*)malloc(header.header.frag_length);
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
return -1;
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_empty_command_write(&buffer[20]); /* Empty command (4 bytes) */
- status = rpc_channel_write(&nextOutChannel->common, buffer, header.header.frag_length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+
+ if (!rts_empty_command_write(buffer)) /* Empty command (4 bytes) */
+ goto fail;
+ status = rts_send_buffer(&nextOutChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return (status) ? 1 : -1;
}
-int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc)
+BOOL rts_send_OUT_R1_A3_pdu(rdpRpc* rpc)
{
- int status;
- BYTE* buffer;
+ BOOL status = FALSE;
+ wStream* buffer;
rpcconn_rts_hdr_t header = rts_pdu_header_init();
UINT32 ReceiveWindowSize;
BYTE* VirtualConnectionCookie;
BYTE* PredecessorChannelCookie;
BYTE* SuccessorChannelCookie;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
- RpcOutChannel* outChannel = connection->DefaultOutChannel;
- RpcOutChannel* nextOutChannel = connection->NonDefaultOutChannel;
+ RpcVirtualConnection* connection;
+ RpcOutChannel* outChannel;
+ RpcOutChannel* nextOutChannel;
+
+ assert(rpc);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
+ outChannel = connection->DefaultOutChannel;
+ assert(outChannel);
+
+ nextOutChannel = connection->NonDefaultOutChannel;
+ assert(nextOutChannel);
header.header.frag_length = 96;
header.Flags = RTS_FLAG_RECYCLE_CHANNEL;
header.NumberOfCommands = 5;
+
WLog_DBG(TAG, "Sending OUT_R1/A3 RTS PDU");
+
VirtualConnectionCookie = (BYTE*)&(connection->Cookie);
PredecessorChannelCookie = (BYTE*)&(outChannel->common.Cookie);
SuccessorChannelCookie = (BYTE*)&(nextOutChannel->common.Cookie);
ReceiveWindowSize = outChannel->ReceiveWindow;
- buffer = (BYTE*)malloc(header.header.frag_length);
+ buffer = Stream_New(NULL, header.header.frag_length);
if (!buffer)
return -1;
- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */
- rts_version_command_write(&buffer[20]); /* Version (8 bytes) */
- rts_cookie_command_write(&buffer[28],
- VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
- rts_cookie_command_write(&buffer[48],
- PredecessorChannelCookie); /* PredecessorChannelCookie (20 bytes) */
- rts_cookie_command_write(&buffer[68],
- SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */
- rts_receive_window_size_command_write(&buffer[88],
- ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
- status = rpc_channel_write(&nextOutChannel->common, buffer, header.header.frag_length);
- free(buffer);
- return (status > 0) ? 1 : -1;
+ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */
+ goto fail;
+ if (!rts_version_command_write(buffer)) /* Version (8 bytes) */
+ goto fail;
+ if (!rts_cookie_command_write(buffer,
+ VirtualConnectionCookie)) /* VirtualConnectionCookie (20 bytes) */
+ goto fail;
+ if (!rts_cookie_command_write(
+ buffer, PredecessorChannelCookie)) /* PredecessorChannelCookie (20 bytes) */
+ goto fail;
+ if (!rts_cookie_command_write(buffer,
+ SuccessorChannelCookie)) /* SuccessorChannelCookie (20 bytes) */
+ goto fail;
+ if (!rts_receive_window_size_command_write(buffer,
+ ReceiveWindowSize)) /* ReceiveWindowSize (8 bytes) */
+ goto fail;
+
+ status = rts_send_buffer(&nextOutChannel->common, buffer, header.header.frag_length);
+fail:
+ Stream_Free(buffer, TRUE);
+ return status;
}
-static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, wStream* buffer)
{
int status;
- UINT32 offset;
UINT32 Destination = 0;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
+ RpcVirtualConnection* connection;
+ assert(rpc);
+ assert(buffer);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
WLog_DBG(TAG, "Receiving OUT R1/A2 RTS PDU");
- offset = 24;
- if (length < offset)
- return -1;
+ status = rts_destination_command_read(rpc, buffer, &Destination);
+ if (status < 0)
+ return status;
- rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination);
connection->NonDefaultOutChannel = rpc_out_channel_new(rpc);
if (!connection->NonDefaultOutChannel)
@@ -859,10 +2036,17 @@ static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
return 1;
}
-static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, wStream* buffer)
{
int status;
- RpcVirtualConnection* connection = rpc->VirtualConnection;
+ RpcVirtualConnection* connection;
+
+ assert(rpc);
+ assert(buffer);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
WLog_DBG(TAG, "Receiving OUT R2/A6 RTS PDU");
status = rts_send_OUT_R2_C1_pdu(rpc);
@@ -887,47 +2071,59 @@ static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
return 1;
}
-static int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+static int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, wStream* buffer)
{
- RpcVirtualConnection* connection = rpc->VirtualConnection;
+ RpcVirtualConnection* connection;
+
+ assert(rpc);
+ assert(buffer);
+
+ connection = rpc->VirtualConnection;
+ assert(connection);
+
WLog_DBG(TAG, "Receiving OUT R2/B3 RTS PDU");
rpc_out_channel_transition_to_state(connection->DefaultOutChannel,
CLIENT_OUT_CHANNEL_STATE_RECYCLED);
return 1;
}
-int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
+BOOL rts_recv_out_of_sequence_pdu(rdpRpc* rpc, wStream* buffer, const rpcconn_hdr_t* header)
{
- int status = -1;
+ BOOL status = FALSE;
UINT32 SignatureId;
- rpcconn_rts_hdr_t* rts;
- RtsPduSignature signature;
+ size_t length, total;
+ RtsPduSignature signature = { 0 };
RpcVirtualConnection* connection;
- if (!rpc || !buffer)
- return -1;
+ assert(rpc);
+ assert(buffer);
+ assert(header);
+
+ total = Stream_Length(buffer);
+ length = header->common.frag_length;
+ if (total < length)
+ return FALSE;
connection = rpc->VirtualConnection;
if (!connection)
- return -1;
-
- rts = (rpcconn_rts_hdr_t*)buffer;
+ return FALSE;
- if (!rts_extract_pdu_signature(&signature, rts))
- return -1;
+ if (!rts_extract_pdu_signature(&signature, buffer, header))
+ return FALSE;
SignatureId = rts_identify_pdu_signature(&signature, NULL);
- if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, rts))
+ if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, buffer, header))
{
- status = rts_recv_flow_control_ack_pdu(rpc, buffer, length);
+ status = rts_recv_flow_control_ack_pdu(rpc, buffer);
}
- else if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, rts))
+ else if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, buffer,
+ header))
{
- status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer, length);
+ status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer);
}
- else if (rts_match_pdu_signature(&RTS_PDU_PING_SIGNATURE, rts))
+ else if (rts_match_pdu_signature(&RTS_PDU_PING_SIGNATURE, buffer, header))
{
status = rts_send_ping_pdu(rpc);
}
@@ -935,28 +2131,28 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
{
if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED)
{
- if (rts_match_pdu_signature(&RTS_PDU_OUT_R1_A2_SIGNATURE, rts))
+ if (rts_match_pdu_signature(&RTS_PDU_OUT_R1_A2_SIGNATURE, buffer, header))
{
- status = rts_recv_OUT_R1_A2_pdu(rpc, buffer, length);
+ status = rts_recv_OUT_R1_A2_pdu(rpc, buffer);
}
}
else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_A6W)
{
- if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_A6_SIGNATURE, rts))
+ if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_A6_SIGNATURE, buffer, header))
{
- status = rts_recv_OUT_R2_A6_pdu(rpc, buffer, length);
+ status = rts_recv_OUT_R2_A6_pdu(rpc, buffer);
}
}
else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_B3W)
{
- if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_B3_SIGNATURE, rts))
+ if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_B3_SIGNATURE, buffer, header))
{
- status = rts_recv_OUT_R2_B3_pdu(rpc, buffer, length);
+ status = rts_recv_OUT_R2_B3_pdu(rpc, buffer);
}
}
}
- if (status < 0)
+ if (!status)
{
WLog_ERR(TAG, "error parsing RTS PDU with signature id: 0x%08" PRIX32 "", SignatureId);
rts_print_pdu_signature(&signature);
@@ -964,3 +2160,42 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
return status;
}
+
+BOOL rts_write_pdu_auth3(wStream* s, const rpcconn_rpc_auth_3_hdr_t* auth)
+{
+ assert(s);
+ assert(auth);
+
+ if (!rts_write_common_pdu_header(s, &auth->header))
+ return FALSE;
+
+ if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT16)))
+ return FALSE;
+
+ Stream_Write_UINT16(s, auth->max_xmit_frag);
+ Stream_Write_UINT16(s, auth->max_recv_frag);
+
+ return rts_write_auth_verifier(s, &auth->auth_verifier, &auth->header);
+}
+
+BOOL rts_write_pdu_bind(wStream* s, const rpcconn_bind_hdr_t* bind)
+{
+
+ assert(s);
+ assert(bind);
+
+ if (!rts_write_common_pdu_header(s, &bind->header))
+ return FALSE;
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT16(s, bind->max_xmit_frag);
+ Stream_Write_UINT16(s, bind->max_recv_frag);
+ Stream_Write_UINT32(s, bind->assoc_group_id);
+
+ if (!rts_write_context_list(s, &bind->p_context_elem))
+ return FALSE;
+
+ return rts_write_auth_verifier(s, &bind->auth_verifier, &bind->header);
+}
diff --git a/libfreerdp/core/gateway/rts.h b/libfreerdp/core/gateway/rts.h
index ccc4cfd18..01a66a781 100644
--- a/libfreerdp/core/gateway/rts.h
+++ b/libfreerdp/core/gateway/rts.h
@@ -24,12 +24,14 @@
#include "config.h"
#endif
-#include "rpc.h"
+#include <winpr/stream.h>
#include <freerdp/api.h>
#include <freerdp/types.h>
#include <freerdp/log.h>
+#include "rpc.h"
+
#define RTS_FLAG_NONE 0x0000
#define RTS_FLAG_PING 0x0001
#define RTS_FLAG_OTHER_CMD 0x0002
@@ -79,21 +81,28 @@
FREERDP_LOCAL void rts_generate_cookie(BYTE* cookie);
-FREERDP_LOCAL SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length);
+FREERDP_LOCAL BOOL rts_write_pdu_auth3(wStream* s, const rpcconn_rpc_auth_3_hdr_t* auth);
+FREERDP_LOCAL BOOL rts_write_pdu_bind(wStream* s, const rpcconn_bind_hdr_t* bind);
+
+FREERDP_LOCAL BOOL rts_read_pdu_header(wStream* s, rpcconn_hdr_t* header);
+FREERDP_LOCAL void rts_free_pdu_header(rpcconn_hdr_t* header, BOOL allocated);
+
+FREERDP_LOCAL BOOL rts_read_common_pdu_header(wStream* s, rpcconn_common_hdr_t* header);
-FREERDP_LOCAL int rts_send_CONN_A1_pdu(rdpRpc* rpc);
-FREERDP_LOCAL int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length);
+FREERDP_LOCAL BOOL rts_command_length(UINT32 CommandType, wStream* s, size_t* length);
-FREERDP_LOCAL int rts_send_CONN_B1_pdu(rdpRpc* rpc);
+FREERDP_LOCAL BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc);
+FREERDP_LOCAL BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer);
-FREERDP_LOCAL int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length);
+FREERDP_LOCAL BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc);
-FREERDP_LOCAL int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc);
+FREERDP_LOCAL BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer);
-FREERDP_LOCAL int rts_send_flow_control_ack_pdu(rdpRpc* rpc);
+FREERDP_LOCAL BOOL rts_send_OUT_R1_A3_pdu(rdpRpc* rpc);
-FREERDP_LOCAL int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length);
+FREERDP_LOCAL BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc);
-#include "rts_signature.h"
+FREERDP_LOCAL BOOL rts_recv_out_of_sequence_pdu(rdpRpc* rpc, wStream* buffer,
+ const rpcconn_hdr_t* header);
#endif /* FREERDP_LIB_CORE_GATEWAY_RTS_H */
diff --git a/libfreerdp/core/gateway/rts_signature.c b/libfreerdp/core/gateway/rts_signature.c
index 4d605f041..b9319d185 100644
--- a/libfreerdp/core/gateway/rts_signature.c
+++ b/libfreerdp/core/gateway/rts_signature.c
@@ -17,6 +17,9 @@
* limitations under the License.
*/
+#include <assert.h>
+#include <winpr/stream.h>
+
#include <freerdp/log.h>
#include "rts_signature.h"
@@ -276,90 +279,74 @@ static const RTS_PDU_SIGNATURE_ENTRY RTS_PDU_SIGNATURE_TABLE[] = {
{ RTS_PDU_PING, TRUE, &RTS_PDU_PING_SIGNATURE, "Ping" },
{ RTS_PDU_FLOW_CONTROL_ACK, TRUE, &RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, "FlowControlAck" },
{ RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION, TRUE,
- &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, "FlowControlAckWithDestination" },
-
- { 0, FALSE, NULL, NULL }
+ &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, "FlowControlAckWithDestination" }
};
-BOOL rts_match_pdu_signature(const RtsPduSignature* signature, const rpcconn_rts_hdr_t* rts)
+BOOL rts_match_pdu_signature(const RtsPduSignature* signature, wStream* src,
+ const rpcconn_hdr_t* header)
{
- UINT16 i;
- int status;
- const BYTE* buffer;
- UINT32 length;
- UINT32 offset;
- UINT32 CommandType;
- UINT32 CommandLength;
-
- if (!signature || !rts)
- return FALSE;
+ RtsPduSignature extracted = { 0 };
- if (rts->Flags != signature->Flags)
- return FALSE;
+ assert(signature);
+ assert(src);
- if (rts->NumberOfCommands != signature->NumberOfCommands)
+ if (!rts_extract_pdu_signature(&extracted, src, header))
return FALSE;
- buffer = (const BYTE*)rts;
- offset = RTS_PDU_HEADER_LENGTH;
- length = rts->header.frag_length - offset;
-
- for (i = 0; i < rts->NumberOfCommands; i++)
- {
- CommandType = *((UINT32*)&buffer[offset]); /* CommandType (4 bytes) */
- offset += 4;
-
- if (CommandType != signature->CommandTypes[i])
- return FALSE;
+ return memcmp(signature, &extracted, sizeof(extracted)) == 0;
+}
- status = rts_command_length(CommandType, &buffer[offset], length);
+BOOL rts_extract_pdu_signature(RtsPduSignature* signature, wStream* src,
+ const rpcconn_hdr_t* header)
+{
+ BOOL rc = FALSE;
+ UINT16 i;
+ wStream tmp;
+ rpcconn_hdr_t rheader = { 0 };
+ const rpcconn_rts_hdr_t* rts;
- if (status < 0)
- return FALSE;
+ assert(signature);
+ assert(src);
- CommandLength = (UINT32)status;
- offset += CommandLength;
- length = rts->header.frag_length - offset;
+ Stream_StaticInit(&tmp, Stream_Pointer(src), Stream_GetRemainingLength(src));
+ if (!header)
+ {
+ if (!rts_read_pdu_header(&tmp, &rheader))
+ goto fail;
+ header = &rheader;
}
-
- return TRUE;
-}
-
-BOOL rts_extract_pdu_signature(RtsPduSignature* signature, const rpcconn_rts_hdr_t* rts)
-{
- int i;
- int status;
- BYTE* buffer;
- UINT32 length;
- UINT32 offset;
- UINT32 CommandType;
- UINT32 CommandLength;
-
- if (!signature || !rts)
- return FALSE;
+ rts = &header->rts;
+ if (rts->header.frag_length < sizeof(rpcconn_rts_hdr_t))
+ goto fail;
signature->Flags = rts->Flags;
signature->NumberOfCommands = rts->NumberOfCommands;
- buffer = (BYTE*)rts;
- offset = RTS_PDU_HEADER_LENGTH;
- length = rts->header.frag_length - offset;
for (i = 0; i < rts->NumberOfCommands; i++)
{
- CommandType = *((UINT32*)&buffer[offset]); /* CommandType (4 bytes) */
- offset += 4;
- signature->CommandTypes[i] = CommandType;
- status = rts_command_length(CommandType, &buffer[offset], length);
+ UINT32 CommandType;
+ size_t CommandLength;
+
+ if (Stream_GetRemainingLength(&tmp) < 4)
+ goto fail;
+
+ Stream_Read_UINT32(&tmp, CommandType); /* CommandType (4 bytes) */
- if (status < 0)
- return FALSE;
+ /* We only need this for comparison against known command types */
+ if (i < ARRAYSIZE(signature->CommandTypes))
+ signature->CommandTypes[i] = CommandType;
- CommandLength = (UINT32)status;
- offset += CommandLength;
- length = rts->header.frag_length - offset;
+ if (!rts_command_length(CommandType, &tmp, &CommandLength))
+ goto fail;
+ if (!Stream_SafeSeek(&tmp, CommandLength))
+ goto fail;
}
- return TRUE;
+ rc = TRUE;
+fail:
+ rts_free_pdu_header(&rheader, FALSE);
+ Stream_Free(&tmp, FALSE);
+ return rc;
}
UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature,
@@ -367,11 +354,15 @@ UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature,
{
size_t i, j;
- for (i = 0; RTS_PDU_SIGNATURE_TABLE[i].SignatureId != 0; i++)
+ if (entry)
+ *entry = NULL;
+
+ for (i = 0; i < ARRAYSIZE(RTS_PDU_SIGNATURE_TABLE); i++)
{
- const RtsPduSignature* pSignature = RTS_PDU_SIGNATURE_TABLE[i].Signature;
+ const RTS_PDU_SIGNATURE_ENTRY* current = &RTS_PDU_SIGNATURE_TABLE[i];
+ const RtsPduSignature* pSignature = current->Signature;
- if (!RTS_PDU_SIGNATURE_TABLE[i].SignatureClient)
+ if (!current->SignatureClient)
continue;
if (signature->Flags != pSignature->Flags)
@@ -387,9 +378,9 @@ UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature,
}
if (entry)
- *entry = &RTS_PDU_SIGNATURE_TABLE[i];
+ *entry = current;
- return RTS_PDU_SIGNATURE_TABLE[i].SignatureId;
+ return current->SignatureId;
}
return 0;
diff --git a/libfreerdp/core/gateway/rts_signature.h b/libfreerdp/core/gateway/rts_signature.h
index 2c43cdc1b..31f0e8100 100644
--- a/libfreerdp/core/gateway/rts_signature.h
+++ b/libfreerdp/core/gateway/rts_signature.h
@@ -178,10 +178,10 @@ FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_PING_SIGNATURE;
FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE;
FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE;
-FREERDP_LOCAL BOOL rts_match_pdu_signature(const RtsPduSignature* signature,
- const rpcconn_rts_hdr_t* rts);
-FREERDP_LOCAL BOOL rts_extract_pdu_signature(RtsPduSignature* signature,
- const rpcconn_rts_hdr_t* rts);
+FREERDP_LOCAL BOOL rts_match_pdu_signature(const RtsPduSignature* signature, wStream* s,
+ const rpcconn_hdr_t* header);
+FREERDP_LOCAL BOOL rts_extract_pdu_signature(RtsPduSignature* signature, wStream* s,
+ const rpcconn_hdr_t* header);
FREERDP_LOCAL UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature,
const RTS_PDU_SIGNATURE_ENTRY** entry);
FREERDP_LOCAL BOOL rts_print_pdu_signature(const RtsPduSignature* signature);
diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c
index 9d01c9fdd..c03f266f2 100644
--- a/libfreerdp/core/gateway/tsg.c
+++ b/libfreerdp/core/gateway/tsg.c
@@ -24,6 +24,7 @@
#include "config.h"
#endif
+#include <assert.h>
#include <winpr/crt.h>
#include <winpr/ndr.h>
#include <winpr/error.h>
@@ -39,13 +40,14 @@
#define TAG FREERDP_TAG("core.gateway.tsg")
+#define TSG_CAPABILITY_TYPE_NAP 0x00000001
+
#define TSG_PACKET_TYPE_HEADER 0x00004844
#define TSG_PACKET_TYPE_VERSIONCAPS 0x00005643
#define TSG_PACKET_TYPE_QUARCONFIGREQUEST 0x00005143
#define TSG_PACKET_TYPE_QUARREQUEST 0x00005152
#define TSG_PACKET_TYPE_RESPONSE 0x00005052
#define TSG_PACKET_TYPE_QUARENC_RESPONSE 0x00004552
-#define TSG_CAPABILITY_TYPE_NAP 0x00000001
#define TSG_PACKET_TYPE_CAPS_RESPONSE 0x00004350
#define TSG_PACKET_TYPE_MSGREQUEST_PACKET 0x00004752
#define TSG_PACKET_TYPE_MESSAGE_PACKET 0x00004750
@@ -222,7 +224,6 @@ struct rdp_tsg
UINT32 TunnelId;
UINT32 ChannelId;
BOOL reauthSequence;
- rdpSettings* settings;
rdpTransport* transport;
UINT64 ReauthTunnelContext;
CONTEXT_HANDLE TunnelContext;
@@ -311,16 +312,35 @@ static BOOL tsg_print(char** buffer, size_t* len, const char* fmt, ...)
static BOOL tsg_packet_header_to_string(char** buffer, size_t* length,
const TSG_PACKET_HEADER* header)
{
+ assert(buffer);
+ assert(length);
+ assert(header);
+
return tsg_print(buffer, length,
"header { ComponentId=0x%04" PRIx16 ", PacketId=0x%04" PRIx16 " }",
header->ComponentId, header->PacketId);
}
+static BOOL tsg_type_capability_nap_to_string(char** buffer, size_t* length,
+ const TSG_CAPABILITY_NAP* cur)
+{
+ assert(buffer);
+ assert(length);
+ assert(cur);
+
+ return tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }",
+ tsg_packet_id_to_string(TSG_CAPABILITY_TYPE_NAP), cur->capabilities);
+}
+
static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length,
const TSG_PACKET_CAPABILITIES* caps, UINT32 numCaps)
{
UINT32 x;
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
if (!tsg_print(buffer, length, "capabilities { "))
return FALSE;
@@ -330,9 +350,7 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length,
switch (cur->capabilityType)
{
case TSG_CAPABILITY_TYPE_NAP:
- if (!tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }",
- tsg_packet_id_to_string(cur->capabilityType),
- cur->tsgPacket.tsgCapNap.capabilities))
+ if (!tsg_type_capability_nap_to_string(buffer, length, &cur->tsgPacket.tsgCapNap))
return FALSE;
break;
default:
@@ -347,6 +365,10 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length,
static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length,
const TSG_PACKET_VERSIONCAPS* caps)
{
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
if (!tsg_print(buffer, length, "versioncaps { "))
return FALSE;
if (!tsg_packet_header_to_string(buffer, length, &caps->tsgHeader))
@@ -368,6 +390,281 @@ static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length,
return tsg_print(buffer, length, " }");
}
+static BOOL tsg_packet_quarconfigrequest_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_QUARCONFIGREQUEST* caps)
+{
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "quarconfigrequest { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " flags=0x%08" PRIx32, caps->flags))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_quarrequest_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_QUARREQUEST* caps)
+{
+ BOOL rc = FALSE;
+ char* name = NULL;
+ char* strdata = NULL;
+
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "quarrequest { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ if (caps->nameLength > 0)
+ {
+ if (caps->nameLength > INT_MAX)
+ return FALSE;
+ if (ConvertFromUnicode(CP_UTF8, 0, caps->machineName, (int)caps->nameLength, &name, 0, NULL,
+ NULL) < 0)
+ return FALSE;
+ }
+
+ strdata = winpr_BinToHexString(caps->data, caps->dataLen, TRUE);
+ if (strdata || (caps->dataLen == 0))
+ rc = tsg_print(buffer, length,
+ " flags=0x%08" PRIx32 ", machineName=%s [%" PRIu32 "], data[%" PRIu32 "]=%s",
+ caps->flags, name, caps->nameLength, caps->dataLen, strdata);
+ free(name);
+ free(strdata);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static const char* tsg_bool_to_string(BOOL val)
+{
+ if (val)
+ return "true";
+ return "false";
+}
+
+static const char* tsg_redirection_flags_to_string(char* buffer, size_t size,
+ const TSG_REDIRECTION_FLAGS* flags)
+{
+ assert(buffer || (size == 0));
+ assert(flags);
+
+ _snprintf(buffer, size,
+ "enableAllRedirections=%s, disableAllRedirections=%s, driveRedirectionDisabled=%s, "
+ "printerRedirectionDisabled=%s, portRedirectionDisabled=%s, reserved=%s, "
+ "clipboardRedirectionDisabled=%s, pnpRedirectionDisabled=%s",
+ tsg_bool_to_string(flags->enableAllRedirections),
+ tsg_bool_to_string(flags->disableAllRedirections),
+ tsg_bool_to_string(flags->driveRedirectionDisabled),
+ tsg_bool_to_string(flags->printerRedirectionDisabled),
+ tsg_bool_to_string(flags->portRedirectionDisabled),
+ tsg_bool_to_string(flags->reserved),
+ tsg_bool_to_string(flags->clipboardRedirectionDisabled),
+ tsg_bool_to_string(flags->pnpRedirectionDisabled));
+ return buffer;
+}
+
+static BOOL tsg_packet_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_RESPONSE* caps)
+{
+ BOOL rc = FALSE;
+ char* strdata = NULL;
+ char tbuffer[8192] = { 0 };
+
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "response { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ strdata = winpr_BinToHexString(caps->responseData, caps->responseDataLen, TRUE);
+ if (strdata || (caps->responseDataLen == 0))
+ rc = tsg_print(
+ buffer, length,
+ " flags=0x%08" PRIx32 ", reserved=0x%08" PRIx32 ", responseData[%" PRIu32
+ "]=%s, redirectionFlags={ %s }",
+ caps->flags, caps->reserved, caps->responseDataLen, strdata,
+ tsg_redirection_flags_to_string(tbuffer, ARRAYSIZE(tbuffer), &caps->redirectionFlags));
+ free(strdata);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_QUARENC_RESPONSE* caps)
+{
+ BOOL rc = FALSE;
+ char* strdata = NULL;
+ RPC_CSTR uuid;
+ char tbuffer[8192] = { 0 };
+ size_t size = ARRAYSIZE(tbuffer);
+ char* ptbuffer = tbuffer;
+
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "quarenc_response { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ if (caps->certChainLen > 0)
+ {
+ if (caps->certChainLen > INT_MAX)
+ return FALSE;
+ if (ConvertFromUnicode(CP_UTF8, 0, caps->certChainData, (int)caps->certChainLen, &strdata,
+ 0, NULL, NULL) <= 0)
+ return FALSE;
+ }
+
+ tsg_packet_versioncaps_to_string(&ptbuffer, &size, caps->versionCaps);
+ UuidToStringA(&caps->nonce, &uuid);
+ if (strdata || (caps->certChainLen == 0))
+ rc =
+ tsg_print(buffer, length,
+ " flags=0x%08" PRIx32 ", certChain[%" PRIu32 "]=%s, nonce=%s, versionCaps=%s",
+ caps->flags, caps->certChainLen, strdata, uuid, tbuffer);
+ free(strdata);
+ free(uuid);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_message_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_MSG_RESPONSE* caps)
+{
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "msg_response { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length,
+ " msgID=0x%08" PRIx32 ", msgType=0x%08" PRIx32 ", isMsgPresent=%" PRId32,
+ caps->msgID, caps->msgType, caps->isMsgPresent))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_caps_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_CAPS_RESPONSE* caps)
+{
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "caps_response { "))
+ return FALSE;
+
+ if (!tsg_packet_quarenc_response_to_string(buffer, length, &caps->pktQuarEncResponse))
+ return FALSE;
+
+ if (!tsg_packet_message_response_to_string(buffer, length, &caps->pktConsentMessage))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_message_request_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_MSG_REQUEST* caps)
+{
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "caps_message_request { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " maxMessagesPerBatch=%" PRIu32, caps->maxMessagesPerBatch))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_auth_to_string(char** buffer, size_t* length, const TSG_PACKET_AUTH* caps)
+{
+ BOOL rc = FALSE;
+ char* strdata = NULL;
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "caps_message_request { "))
+ return FALSE;
+
+ if (!tsg_packet_versioncaps_to_string(buffer, length, &caps->tsgVersionCaps))
+ return FALSE;
+
+ strdata = winpr_BinToHexString(caps->cookie, caps->cookieLen, TRUE);
+ if (strdata || (caps->cookieLen == 0))
+ rc = tsg_print(buffer, length, " cookie[%" PRIu32 "]=%s", caps->cookieLen, strdata);
+ free(strdata);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_reauth_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_REAUTH* caps)
+{
+ BOOL rc = FALSE;
+ assert(buffer);
+ assert(length);
+ assert(caps);
+
+ if (!tsg_print(buffer, length, "caps_message_request { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " tunnelContext=0x%016" PRIx64 ", packetId=%s [0x%08" PRIx32 "]",
+ caps->tunnelContext, tsg_packet_id_to_string(caps->packetId), caps->packetId))
+ return FALSE;
+
+ switch (caps->packetId)
+ {
+ case TSG_PACKET_TYPE_VERSIONCAPS:
+ rc = tsg_packet_versioncaps_to_string(buffer, length,
+ caps->tsgInitialPacket.packetVersionCaps);
+ break;
+ case TSG_PACKET_TYPE_AUTH:
+ rc = tsg_packet_auth_to_string(buffer, length, caps->tsgInitialPacket.packetAuth);
+ break;
+ default:
+ rc = tsg_print(buffer, length, "TODO: Unhandled packet type %s [0x%08" PRIx32 "]",
+ tsg_packet_id_to_string(caps->packetId), caps->packetId);
+ break;
+ }
+
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
static const char* tsg_packet_to_string(const TSG_PACKET* packet)
{
size_t len = 8192;
@@ -390,43 +687,45 @@ static const char* tsg_packet_to_string(const TSG_PACKET* packet)
goto fail;
break;
case TSG_PACKET_TYPE_QUARCONFIGREQUEST:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_quarconfigrequest_to_string(&buffer, &len,
+ packet->tsgPacket.packetQuarConfigRequest))
goto fail;
break;
case TSG_PACKET_TYPE_QUARREQUEST:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_quarrequest_to_string(&buffer, &len,
+ packet->tsgPacket.packetQuarRequest))
goto fail;
break;
case TSG_PACKET_TYPE_RESPONSE:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_response_to_string(&buffer, &len, packet->tsgPacket.packetResponse))
goto fail;
break;
case TSG_PACKET_TYPE_QUARENC_RESPONSE:
- if (!tsg_print(&buffer, &len, "TODO"))
- goto fail;
- break;
- case TSG_CAPABILITY_TYPE_NAP:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_quarenc_response_to_string(&buffer, &len,
+ packet->tsgPacket.packetQuarEncResponse))
goto fail;
break;
case TSG_PACKET_TYPE_CAPS_RESPONSE:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_caps_response_to_string(&buffer, &len,
+ packet->tsgPacket.packetCapsResponse))
goto fail;
break;
case TSG_PACKET_TYPE_MSGREQUEST_PACKET:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_message_request_to_string(&buffer, &len,
+ packet->tsgPacket.packetMsgRequest))
goto fail;
break;
case TSG_PACKET_TYPE_MESSAGE_PACKET:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_message_response_to_string(&buffer, &len,
+ packet->tsgPacket.packetMsgResponse))
goto fail;
break;
case TSG_PACKET_TYPE_AUTH:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_auth_to_string(&buffer, &len, packet->tsgPacket.packetAuth))
goto fail;
break;
case TSG_PACKET_TYPE_REAUTH:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_reauth_to_string(&buffer, &len, packet->tsgPacket.packetReauth))
goto fail;
break;
default:
@@ -500,7 +799,7 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI
{
wStream* s;
rdpTsg* tsg;
- int length;
+ size_t length;
const byte* buffer1 = NULL;
const byte* buffer2 = NULL;
const byte* buffer3 = NULL;
@@ -536,7 +835,9 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI
totalDataBytes += lengths[2] + 4;
}
- length = 28 + totalDataBytes;
+ length = 28ull + totalDataBytes;
+ if (length > INT_MAX)
+ return -1;
s = Stream_New(NULL, length);
if (!s)
@@ -572,7 +873,7 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI
if (!rpc_client_write_call(tsg->rpc, s, TsProxySendToServerOpnum))
return -1;
- return length;
+ return (int)length;
}
/**
@@ -725,12 +1026,19 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu,
UINT32 SwitchValue;
UINT32 MessageSwitchValue = 0;
UINT32 IsMessagePresent;
+ rdpContext* context;
PTSG_PACKET_CAPABILITIES tsgCaps = NULL;
PTSG_PACKET_VERSIONCAPS versionCaps = NULL;
TSG_PACKET_STRING_MESSAGE packetStringMessage;
PTSG_PACKET_CAPS_RESPONSE packetCapsResponse = NULL;
PTSG_PACKET_QUARENC_RESPONSE packetQuarEncResponse = NULL;
+ assert(tsg);
+ assert(tsg->rpc);
+
+ context = tsg->rpc->context;
+ assert(context);
+
if (!pdu)
return FALSE;
@@ -877,8 +1185,8 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu,
if (Stream_GetRemainingLength(pdu->s) < 16)
goto fail;
- Stream_Read_UINT32(pdu->s, packetStringMessage.isDisplayMandatory);
- Stream_Read_UINT32(pdu->s, packetStringMessage.isConsentMandatory);
+ Stream_Read_INT32(pdu->s, packetStringMessage.isDisplayMandatory);
+ Stream_Read_INT32(pdu->s, packetStringMessage.isConsentMandatory);
Stream_Read_UINT32(pdu->s, packetStringMessage.msgBytes);
Stream_Read_UINT32(pdu->s, Pointer);
@@ -907,16 +1215,15 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu,
goto fail;
}
- if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance)
+ if (context->instance)
{
- rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage,
- tsg->rpc->context->instance,
- TSG_ASYNC_MESSAGE_CONSENT_MESSAGE
- ? GATEWAY_MESSAGE_CONSENT
- : TSG_ASYNC_MESSAGE_SERVICE_MESSAGE,
- packetStringMessage.isDisplayMandatory != 0,
- packetStringMessage.isConsentMandatory != 0,
- packetStringMessage.msgBytes, packetStringMessage.msgBuffer);
+ rc = IFCALLRESULT(
+ TRUE, context->instance->PresentGatewayMessage, context->instance,
+ TSG_ASYNC_MESSAGE_CONSENT_MESSAGE ? GATEWAY_MESSAGE_CONSENT
+ : TSG_ASYNC_MESSAGE_SERVICE_MESSAGE,
+ packetStringMessage.isDisplayMandatory != 0,
+ packetStringMessage.isConsentMandatory != 0, packetStringMessage.msgBytes,
+ packetStringMessage.msgBuffer);
if (!rc)
goto fail;
}
@@ -1079,16 +1386,19 @@ fail:
static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnelContext)
{
- UINT32 pad;
+ size_t pad;
wStream* s;
size_t count;
- UINT32 offset;
+ size_t offset;
rdpRpc* rpc;
if (!tsg || !tsg->rpc || !tunnelContext || !tsg->MachineName)
return FALSE;
count = _wcslen(tsg->MachineName) + 1;
+ if (count > UINT32_MAX)
+ return FALSE;
+
rpc = tsg->rpc;
WLog_DBG(TAG, "TsProxyAuthorizeTunnelWriteRequest");
s = Stream_New(NULL, 1024 + count * 2);
@@ -1105,13 +1415,13 @@ static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunn
Stream_Write_UINT32(s, 0x00020000); /* PacketQuarRequestPtr (4 bytes) */
Stream_Write_UINT32(s, 0x00000000); /* Flags (4 bytes) */
Stream_Write_UINT32(s, 0x00020004); /* MachineNamePtr (4 bytes) */
- Stream_Write_UINT32(s, count); /* NameLength (4 bytes) */
+ Stream_Write_UINT32(s, (UINT32)count); /* NameLength (4 bytes) */
Stream_Write_UINT32(s, 0x00020008); /* DataPtr (4 bytes) */
Stream_Write_UINT32(s, 0); /* DataLength (4 bytes) */
/* MachineName */
- Stream_Write_UINT32(s, count); /* MaxCount (4 bytes) */
+ Stream_Write_UINT32(s, (UINT32)count); /* MaxCount (4 bytes) */
Stream_Write_UINT32(s, 0); /* Offset (4 bytes) */
- Stream_Write_UINT32(s, count); /* ActualCount (4 bytes) */
+ Stream_Write_UINT32(s, (UINT32)count); /* ActualCount (4 bytes) */
Stream_Write_UTF16_String(s, tsg->MachineName, count); /* Array */
/* 4-byte alignment */
offset = Stream_GetPosition(s);
@@ -1122,7 +1432,7 @@ static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunn
return rpc_client_write_call(rpc, s, TsProxyAuthorizeTunnelOpnum);
}
-static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu)
+static BOOL TsProxyAuthorizeTunnelReadResponse(RPC_PDU* pdu)
{
BOOL rc = FALSE;
UINT32 Pointer;
@@ -1184,25 +1494,24 @@ static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu)
Stream_Seek_UINT32(pdu->s); /* Reserved (4 bytes) */
Stream_Read_UINT32(pdu->s, Pointer); /* ResponseDataPtr (4 bytes) */
Stream_Read_UINT32(pdu->s, packetResponse->responseDataLen); /* ResponseDataLength (4 bytes) */
- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags
- .enableAllRedirections); /* EnableAllRedirections (4 bytes) */
- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags
- .disableAllRedirections); /* DisableAllRedirections (4 bytes) */
- Stream_Read_UINT32(pdu->s,
- packetResponse->redirectionFlags
- .driveRedirectionDisabled); /* DriveRedirectionDisabled (4 bytes) */
- Stream_Read_UINT32(pdu->s,
- packetResponse->redirectionFlags
- .printerRedirectionDisabled); /* PrinterRedirectionDisabled (4 bytes) */
- Stream_Read_UINT32(pdu->s,
- packetResponse->redirectionFlags
- .portRedirectionDisabled); /* PortRedirectionDisabled (4 bytes) */
- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags.reserved); /* Reserved (4 bytes) */
- Stream_Read_UINT32(
+ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags
+ .enableAllRedirections); /* EnableAllRedirections (4 bytes) */
+ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags
+ .disableAllRedirections); /* DisableAllRedirections (4 bytes) */
+ Stream_Read_INT32(pdu->s,
+ packetResponse->redirectionFlags
+ .driveRedirectionDisabled); /* DriveRedirectionDisabled (4 bytes) */
+ Stream_Read_INT32(pdu->s,
+ packetResponse->redirectionFlags
+ .printerRedirectionDisabled); /* PrinterRedirectionDisabled (4 bytes) */
+ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags
+ .portRedirectionDisabled); /* PortRedirectionDisabled (4 bytes) */
+ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags.reserved); /* Reserved (4 bytes) */
+ Stream_Read_INT32(
pdu->s, packetResponse->redirectionFlags
.clipboardRedirectionDisabled); /* ClipboardRedirectionDisabled (4 bytes) */
- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags
- .pnpRedirectionDisabled); /* PnpRedirectionDisabled (4 bytes) */
+ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags
+ .pnpRedirectionDisabled); /* PnpRedirectionDisabled (4 bytes) */
Stream_Read_UINT32(pdu->s, SizeValue); /* (4 bytes) */
if (SizeValue != packetResponse->responseDataLen)
@@ -1302,11 +1611,18 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu)
UINT32 Pointer;
UINT32 SwitchValue;
TSG_PACKET packet;
+ rdpContext* context;
char* messageText = NULL;
TSG_PACKET_MSG_RESPONSE packetMsgResponse = { 0 };
TSG_PACKET_STRING_MESSAGE packetStringMessage = { 0 };
TSG_PACKET_REAUTH_MESSAGE packetReauthMessage = { 0 };
+ assert(tsg);
+ assert(tsg->rpc);
+
+ context = tsg->rpc->context;
+ assert(context);
+
/* This is an asynchronous response */
if (!pdu)
@@ -1356,10 +1672,10 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu)
WLog_INFO(TAG, "Consent Message: %s", messageText);
free(messageText);
- if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance)
+ if (context->instance)
{
- rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage,
- tsg->rpc->context->instance, GATEWAY_MESSAGE_CONSENT,
+ rc = IFCALLRESULT(TRUE, context->instance->PresentGatewayMessage, context->instance,
+ GATEWAY_MESSAGE_CONSENT,
packetStringMessage.isDisplayMandatory != 0,
packetStringMessage.isConsentMandatory != 0,
packetStringMessage.msgBytes, packetStringMessage.msgBuffer);
@@ -1377,10 +1693,10 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu)
WLog_INFO(TAG, "Service Message: %s", messageText);
free(messageText);
- if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance)
+ if (context->instance)
{
- rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage,
- tsg->rpc->context->instance, GATEWAY_MESSAGE_SERVICE,
+ rc = IFCALLRESULT(TRUE, context->instance->PresentGatewayMessage, context->instance,
+ GATEWAY_MESSAGE_SERVICE,
packetStringMessage.isDisplayMandatory != 0,
packetStringMessage.isConsentMandatory != 0,
packetStringMessage.msgBytes, packetStringMessage.msgBuffer);
@@ -1432,6 +1748,8 @@ static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnel
rpc = tsg->rpc;
count = _wcslen(tsg->Hostname) + 1;
+ if (count > UINT32_MAX)
+ return FALSE;
s = Stream_New(NULL, 60 + count * 2);
if (!s)
@@ -1451,15 +1769,15 @@ static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnel
Stream_Write_UINT16(s, tsg->Port); /* PortNumber (0xD3D = 3389) (2 bytes) */
Stream_Write_UINT32(s, 0x00000001); /* NumResourceNames (4 bytes) */
Stream_Write_UINT32(s, 0x00020004); /* ResourceNamePtr (4 bytes) */
- Stream_Write_UINT32(s, count); /* MaxCount (4 bytes) */
+ Stream_Write_UINT32(s, (UINT32)count); /* MaxCount (4 bytes) */
Stream_Write_UINT32(s, 0); /* Offset (4 bytes) */
- Stream_Write_UINT32(s, count); /* ActualCount (4 bytes) */
+ Stream_Write_UINT32(s, (UINT32)count); /* ActualCount (4 bytes) */
Stream_Write_UTF16_String(s, tsg->Hostname, count); /* Array */
return rpc_client_write_call(rpc, s, TsProxyCreateChannelOpnum);
}
-static BOOL TsProxyCreateChannelReadResponse(rdpTsg* tsg, RPC_PDU* pdu,
- CONTEXT_HANDLE* channelContext, UINT32* channelId)
+static BOOL TsProxyCreateChannelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* channelContext,
+ UINT32* channelId)
{
BOOL rc = FALSE;
WLog_DBG(TAG, "TsProxyCreateChannelReadResponse");
@@ -1507,7 +1825,7 @@ static BOOL TsProxyCloseChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context
return rpc_client_write_call(rpc, s, TsProxyCloseChannelOpnum);
}
-static BOOL TsProxyCloseChannelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, CONTEXT_HANDLE* context)
+static BOOL TsProxyCloseChannelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* context)
{
BOOL rc = FALSE;
WLog_DBG(TAG, "TsProxyCloseChannelReadResponse");
@@ -1554,7 +1872,7 @@ static BOOL TsProxyCloseTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context)
return rpc_client_write_call(rpc, s, TsProxyCloseTunnelOpnum);
}
-static BOOL TsProxyCloseTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, CONTEXT_HANDLE* context)
+static BOOL TsProxyCloseTunnelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* context)
{
BOOL rc = FALSE;
WLog_DBG(TAG, "TsProxyCloseTunnelReadResponse");
@@ -1705,8 +2023,6 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
return FALSE;
rpc = tsg->rpc;
- Stream_SealLength(pdu->s);
- Stream_SetPosition(pdu->s, 0);
if (!(pdu->Flags & RPC_PDU_FLAG_STUB))
{
@@ -1745,7 +2061,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
CONTEXT_HANDLE* TunnelContext;
TunnelContext = (tsg->reauthSequence) ? &tsg->NewTunnelContext : &tsg->TunnelContext;
- if (!TsProxyAuthorizeTunnelReadResponse(tsg, pdu))
+ if (!TsProxyAuthorizeTunnelReadResponse(pdu))
{
WLog_ERR(TAG, "TsProxyAuthorizeTunnelReadResponse failure");
return FALSE;
@@ -1794,7 +2110,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
{
CONTEXT_HANDLE ChannelContext;
- if (!TsProxyCreateChannelReadResponse(tsg, pdu, &ChannelContext, &tsg->ChannelId))
+ if (!TsProxyCreateChannelReadResponse(pdu, &ChannelContext, &tsg->ChannelId))
{
WLog_ERR(TAG, "TsProxyCreateChannelReadResponse failure");
return FALSE;
@@ -1867,7 +2183,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
{
CONTEXT_HANDLE ChannelContext;
- if (!TsProxyCloseChannelReadResponse(tsg, pdu, &ChannelContext))
+ if (!TsProxyCloseChannelReadResponse(pdu, &ChannelContext))
{
WLog_ERR(TAG, "TsProxyCloseChannelReadResponse failure");
return FALSE;
@@ -1879,7 +2195,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
{
CONTEXT_HANDLE TunnelContext;
- if (!TsProxyCloseTunnelReadResponse(tsg, pdu, &TunnelContext))
+ if (!TsProxyCloseTunnelReadResponse(pdu, &TunnelContext))
{
WLog_ERR(TAG, "TsProxyCloseTunnelReadResponse failure");
return FALSE;
@@ -1894,7 +2210,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
{
CONTEXT_HANDLE ChannelContext;
- if (!TsProxyCloseChannelReadResponse(tsg, pdu, &ChannelContext))
+ if (!TsProxyCloseChannelReadResponse(pdu, &ChannelContext))
{
WLog_ERR(TAG, "TsProxyCloseChannelReadResponse failure");
return FALSE;
@@ -1924,7 +2240,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu)
{
CONTEXT_HANDLE TunnelContext;
- if (!TsProxyCloseTunnelReadResponse(tsg, pdu, &TunnelContext))
+ if (!TsProxyCloseTunnelReadResponse(pdu, &TunnelContext))
{
WLog_ERR(TAG, "TsProxyCloseTunnelReadResponse failure");
return FALSE;
@@ -2033,10 +2349,25 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout)
{
UINT64 looptimeout = timeout * 1000ULL;
DWORD nCount;
- HANDLE events[64];
- rdpRpc* rpc = tsg->rpc;
- rdpSettings* settings = rpc->settings;
- rdpTransport* transport = rpc->transport;
+ HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 };
+ rdpRpc* rpc;
+ rdpContext* context;
+ rdpSettings* settings;
+ rdpTransport* transport;
+
+ assert(tsg);
+
+ rpc = tsg->rpc;
+ assert(rpc);
+
+ transport = rpc->transport;
+ assert(transport);
+
+ context = tsg->rpc->context;
+ assert(context);
+
+ settings = context->settings;
+
tsg->Port = port;
tsg->transport = transport;
@@ -2055,7 +2386,7 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout)
return FALSE;
}
- nCount = tsg_get_event_handles(tsg, events, 64);
+ nCount = tsg_get_event_handles(tsg, events, ARRAYSIZE(events));
if (nCount == 0)
return FALSE;
@@ -2138,7 +2469,7 @@ BOOL tsg_disconnect(rdpTsg* tsg)
* @return < 0 on error; 0 if not enough data is available (non blocking mode); > 0 bytes to read
*/
-static int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length)
+static int tsg_read(rdpTsg* tsg, BYTE* data, size_t length)
{
rdpRpc* rpc;
int status = 0;
@@ -2156,7 +2487,7 @@ static int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length)
do
{
- status = rpc_client_receive_pipe_read(rpc->client, data, (size_t)length);
+ status = rpc_client_receive_pipe_read(rpc->client, data, length);
if (status < 0)
return -1;
@@ -2206,7 +2537,7 @@ static int tsg_write(rdpTsg* tsg, const BYTE* data, UINT32 length)
if (status < 0)
return -1;
- return length;
+ return (int)length;
}
rdpTsg* tsg_new(rdpTransport* transport)
@@ -2218,7 +2549,6 @@ rdpTsg* tsg_new(rdpTransport* transport)
return NULL;
tsg->transport = transport;
- tsg->settings = transport->settings;
tsg->rpc = rpc_new(tsg->transport);
if (!tsg->rpc)
@@ -2246,7 +2576,10 @@ static int transport_bio_tsg_write(BIO* bio, const char* buf, int num)
int status;
rdpTsg* tsg = (rdpTsg*)BIO_get_data(bio);
BIO_clear_flags(bio, BIO_FLAGS_WRITE);
- status = tsg_write(tsg, (BYTE*)buf, num);
+
+ if (num < 0)
+ return -1;
+ status = tsg_write(tsg, (const BYTE*)buf, (UINT32)num);
if (status < 0)
{
@@ -2278,7 +2611,7 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size)
}
BIO_clear_flags(bio, BIO_FLAGS_READ);
- status = tsg_read(tsg, (BYTE*)buf, size);
+ status = tsg_read(tsg, (BYTE*)buf, (size_t)size);
if (status < 0)
{
@@ -2300,17 +2633,22 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size)
static int transport_bio_tsg_puts(BIO* bio, const char* str)
{
+ WINPR_UNUSED(bio);
+ WINPR_UNUSED(str);
return 1;
}
static int transport_bio_tsg_gets(BIO* bio, char* str, int size)
{
+ WINPR_UNUSED(bio);
+ WINPR_UNUSED(str);
+ WINPR_UNUSED(size);
return 1;
}
static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
{
- int status = -1;
+ long status = -1;
rdpTsg* tsg = (rdpTsg*)BIO_get_data(bio);
RpcVirtualConnection* connection = tsg->rpc->VirtualConnection;
RpcInChannel* inChannel = connection->DefaultInChannel;
@@ -2339,27 +2677,27 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
case BIO_C_READ_BLOCKED:
{
- BIO* bio = outChannel->common.bio;
- status = BIO_read_blocked(bio);
+ BIO* cbio = outChannel->common.bio;
+ status = BIO_read_blocked(cbio);
}
break;
case BIO_C_WRITE_BLOCKED:
{
- BIO* bio = inChannel->common.bio;
- status = BIO_write_blocked(bio);
+ BIO* cbio = inChannel->common.bio;
+ status = BIO_write_blocked(cbio);
}
break;
case BIO_C_WAIT_READ:
{
int timeout = (int)arg1;
- BIO* bio = outChannel->common.bio;
+ BIO* cbio = outChannel->common.bio;
- if (BIO_read_blocked(bio))
- return BIO_wait_read(bio, timeout);
- else if (BIO_write_blocked(bio))
- return BIO_wait_write(bio, timeout);
+ if (BIO_read_blocked(cbio))
+ return BIO_wait_read(cbio, timeout);
+ else if (BIO_write_blocked(cbio))
+ return BIO_wait_write(cbio, timeout);
else
status = 1;
}
@@ -2368,12 +2706,12 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
case BIO_C_WAIT_WRITE:
{
int timeout = (int)arg1;
- BIO* bio = inChannel->common.bio;
+ BIO* cbio = inChannel->common.bio;
- if (BIO_write_blocked(bio))
- status = BIO_wait_write(bio, timeout);
- else if (BIO_read_blocked(bio))
- status = BIO_wait_read(bio, timeout);
+ if (BIO_write_blocked(cbio))
+ status = BIO_wait_write(cbio, timeout);
+ else if (BIO_read_blocked(cbio))
+ status = BIO_wait_read(cbio, timeout);
else
status = 1;
}
@@ -2388,6 +2726,7 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
static int transport_bio_tsg_new(BIO* bio)
{
+ assert(bio);
BIO_set_init(bio, 1);
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
return 1;
@@ -2395,6 +2734,8 @@ static int transport_bio_tsg_new(BIO* bio)
static int transport_bio_tsg_free(BIO* bio)
{
+ assert(bio);
+ WINPR_UNUSED(bio);
return 1;
}
diff --git a/libfreerdp/core/orders.c b/libfreerdp/core/orders.c
index 0e14bbe6a..34ccbb7b0 100644
--- a/libfreerdp/core/orders.c
+++ b/libfreerdp/core/orders.c
@@ -1881,6 +1881,13 @@ static BOOL update_read_fast_glyph_order(wStream* s, const ORDER_INFO* orderInfo
!update_read_2byte_unsigned(&sub, &glyph->cy))
return FALSE;
+ if ((glyph->cx == 0) || (glyph->cy == 0))
+ {
+ WLog_ERR(TAG, "GLYPH_DATA_V2::cx=%" PRIu32 ", GLYPH_DATA_V2::cy=%" PRIu32,
+ glyph->cx, glyph->cy);
+ return FALSE;
+ }
+
glyph->cb = Stream_GetRemainingLength(&sub);
if (glyph->cb > 0)
{
@@ -2867,6 +2874,13 @@ update_read_create_offscreen_bitmap_order(wStream* s,
Stream_Read_UINT16(s, create_offscreen_bitmap->cy); /* cy (2 bytes) */
deleteList = &(create_offscreen_bitmap->deleteList);
+ if ((create_offscreen_bitmap->cx == 0) || (create_offscreen_bitmap->cy == 0))
+ {
+ WLog_ERR(TAG, "Invalid OFFSCREEN_DELETE_LIST: cx=%" PRIu16 ", cy=%" PRIu16,
+ create_offscreen_bitmap->cx, create_offscreen_bitmap->cy);
+ return FALSE;
+ }
+
if (deleteListPresent)
{
UINT32 i;
diff --git a/libfreerdp/core/surface.c b/libfreerdp/core/surface.c
index d5c709885..ca89d230c 100644
--- a/libfreerdp/core/surface.c
+++ b/libfreerdp/core/surface.c
@@ -21,6 +21,8 @@
#include "config.h"
#endif
+#include <assert.h>
+
#include <freerdp/utils/pcap.h>
#include <freerdp/log.h>
@@ -62,6 +64,13 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp)
Stream_Read_UINT16(s, bmp->height);
Stream_Read_UINT32(s, bmp->bitmapDataLength);
+ if ((bmp->width == 0) || (bmp->height == 0))
+ {
+ WLog_ERR(TAG, "invalid size value width=%" PRIu16 ", height=%" PRIu16, bmp->width,
+ bmp->height);
+ return FALSE;
+ }
+
if ((bmp->bpp < 1) || (bmp->bpp > 32))
{
WLog_ERR(TAG, "invalid bpp value %" PRIu32 "", bmp->bpp);
@@ -85,6 +94,39 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp)
return TRUE;
}
+static BOOL update_recv_surfcmd_is_rect_valid(const rdpContext* context,
+ const SURFACE_BITS_COMMAND* cmd)
+{
+ assert(context);
+ assert(context->settings);
+ assert(cmd);
+
+ /* We need a rectangle with left/top being smaller than right/bottom.
+ * Also do not allow empty rectangles. */
+ if ((cmd->destTop >= cmd->destBottom) || (cmd->destLeft >= cmd->destRight))
+ {
+ WLog_WARN(TAG,
+ "Empty surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16
+ "x%" PRIu16,
+ cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom);
+ return FALSE;
+ }
+
+ /* The rectangle needs to fit into our session size */
+ if ((cmd->destRight > context->settings->DesktopWidth) ||
+ (cmd->destBottom > context->settings->DesktopHeight))
+ {
+ WLog_WARN(TAG,
+ "Invalid surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16
+ "x%" PRIu16 " does not fit %" PRIu32 "x%" PRIu32,
+ cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom,
+ context->settings->DesktopWidth, context->settings->DesktopHeight);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT16 cmdType)
{
SURFACE_BITS_COMMAND cmd = { 0 };
@@ -98,6 +140,9 @@ static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT
Stream_Read_UINT16(s, cmd.destRight);
Stream_Read_UINT16(s, cmd.destBottom);
+ if (!update_recv_surfcmd_is_rect_valid(update->context, &cmd))
+ goto fail;
+
if (!update_recv_surfcmd_bitmap_ex(s, &cmd.bmp))
goto fail;
diff --git a/libfreerdp/core/update.c b/libfreerdp/core/update.c
index ebb82fc2d..e137c3de8 100644
--- a/libfreerdp/core/update.c
+++ b/libfreerdp/core/update.c
@@ -99,6 +99,13 @@ static BOOL update_read_bitmap_data(rdpUpdate* update, wStream* s, BITMAP_DATA*
Stream_Read_UINT16(s, bitmapData->flags);
Stream_Read_UINT16(s, bitmapData->bitmapLength);
+ if ((bitmapData->width == 0) || (bitmapData->height == 0))
+ {
+ WLog_ERR(TAG, "Invalid BITMAP_DATA: width=%" PRIu16 ", height=%" PRIu16, bitmapData->width,
+ bitmapData->height);
+ return FALSE;
+ }
+
if (bitmapData->flags & BITMAP_COMPRESSION)
{
if (!(bitmapData->flags & NO_BITMAP_COMPRESSION_HDR))
diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt
index 0cb8845b0..036b50f83 100644
--- a/winpr/CMakeLists.txt
+++ b/winpr/CMakeLists.txt
@@ -51,7 +51,7 @@ if (NOT WIN32)
endif()
# Soname versioning
-set(RAW_VERSION_STRING "2.4.0")
+set(RAW_VERSION_STRING "2.4.1")
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
elseif(USE_VERSION_FROM_GIT_TAG)
diff --git a/winpr/libwinpr/crt/utf.c b/winpr/libwinpr/crt/utf.c
index 8c8d2a841..2cbc5987b 100644
--- a/winpr/libwinpr/crt/utf.c
+++ b/winpr/libwinpr/crt/utf.c
@@ -582,7 +582,7 @@ ConversionResult ConvertUTF8toUTF16(const BYTE** sourceStart, const BYTE* source
ch -= offsetsFromUTF8[extraBytesToRead];
- if ((target >= end) && (!computeLength))
+ if ((target * sizeof(WCHAR) >= end) && (!computeLength))
{
source -= (extraBytesToRead + 1); /* Back up source pointer! */
result = targetExhausted;
@@ -633,7 +633,7 @@ ConversionResult ConvertUTF8toUTF16(const BYTE** sourceStart, const BYTE* source
else
{
/* target is a character in range 0xFFFF - 0x10FFFF. */
- if ((target + 1 >= end) && (!computeLength))
+ if (((target + 1) * sizeof(WCHAR) >= end) && (!computeLength))
{
source -= (extraBytesToRead + 1); /* Back up source pointer! */
result = targetExhausted;
diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c
index 1f8bf9935..aa53f68fc 100644
--- a/winpr/libwinpr/file/generic.c
+++ b/winpr/libwinpr/file/generic.c
@@ -539,15 +539,80 @@ DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
return ret;
}
+static char* append(char* buffer, size_t size, const char* append)
+{
+ const size_t len = strnlen(buffer, size);
+ if (len == 0)
+ _snprintf(buffer, size, "%s", append);
+ else
+ {
+ strcat(buffer, "|");
+ strcat(buffer, append);
+ }
+
+ return buffer;
+}
+
+static const char* flagsToStr(char* buffer, size_t size, DWORD flags)
+{
+ char strflags[32] = { 0 };
+ if (flags & FILE_ATTRIBUTE_READONLY)
+ append(buffer, size, "FILE_ATTRIBUTE_READONLY");
+ if (flags & FILE_ATTRIBUTE_HIDDEN)
+ append(buffer, size, "FILE_ATTRIBUTE_HIDDEN");
+ if (flags & FILE_ATTRIBUTE_SYSTEM)
+ append(buffer, size, "FILE_ATTRIBUTE_SYSTEM");
+ if (flags & FILE_ATTRIBUTE_DIRECTORY)
+ append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY");
+ if (flags & FILE_ATTRIBUTE_ARCHIVE)
+ append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE");
+ if (flags & FILE_ATTRIBUTE_DEVICE)
+ append(buffer, size, "FILE_ATTRIBUTE_DEVICE");
+ if (flags & FILE_ATTRIBUTE_NORMAL)
+ append(buffer, size, "FILE_ATTRIBUTE_NORMAL");
+ if (flags & FILE_ATTRIBUTE_TEMPORARY)
+ append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY");
+ if (flags & FILE_ATTRIBUTE_SPARSE_FILE)
+ append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE");
+ if (flags & FILE_ATTRIBUTE_REPARSE_POINT)
+ append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT");
+ if (flags & FILE_ATTRIBUTE_COMPRESSED)
+ append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED");
+ if (flags & FILE_ATTRIBUTE_OFFLINE)
+ append(buffer, size, "FILE_ATTRIBUTE_OFFLINE");
+ if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED");
+ if (flags & FILE_ATTRIBUTE_ENCRYPTED)
+ append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED");
+ if (flags & FILE_ATTRIBUTE_VIRTUAL)
+ append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL");
+
+ _snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags);
+ strcat(buffer, strflags);
+ return buffer;
+}
+
BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
{
struct stat st;
+ int fd;
+ BOOL rc = FALSE;
- if (stat(lpFileName, &st) != 0)
+ if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
{
- return FALSE;
+ char buffer[8192] = { 0 };
+ const char* flags =
+ flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+ WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags);
}
+ fd = open(lpFileName, O_RDONLY);
+ if (fd < 0)
+ return FALSE;
+
+ if (fstat(fd, &st) != 0)
+ goto fail;
+
if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
{
st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
@@ -557,12 +622,13 @@ BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
st.st_mode |= S_IWUSR;
}
- if (chmod(lpFileName, st.st_mode) != 0)
- {
- return FALSE;
- }
+ if (fchmod(fd, st.st_mode) != 0)
+ goto fail;
- return TRUE;
+ rc = TRUE;
+fail:
+ close(fd);
+ return rc;
}
BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
@@ -570,6 +636,14 @@ BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
BOOL ret;
LPSTR lpCFileName;
+ if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
+ {
+ char buffer[8192] = { 0 };
+ const char* flags =
+ flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+ WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags);
+ }
+
if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
diff --git a/winpr/libwinpr/file/test/CMakeLists.txt b/winpr/libwinpr/file/test/CMakeLists.txt
index c7015c670..399927110 100644
--- a/winpr/libwinpr/file/test/CMakeLists.txt
+++ b/winpr/libwinpr/file/test/CMakeLists.txt
@@ -1,54 +1,58 @@
-set(MODULE_NAME "TestFile")
-set(MODULE_PREFIX "TEST_FILE")
+if (NOT WIN32)
+ set(MODULE_NAME "TestFile")
+ set(MODULE_PREFIX "TEST_FILE")
+
+ set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+ set(${MODULE_PREFIX}_TESTS
+ TestFileCreateFile.c
+ TestFileDeleteFile.c
+ TestFileReadFile.c
+ TestSetFileAttributes.c
+ TestFileWriteFile.c
+ TestFilePatternMatch.c
+ TestFileFindFirstFile.c
+ TestFileFindFirstFileEx.c
+ TestFileFindNextFile.c
+ TestFileGetStdHandle.c
+ )
+
+ create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+ target_link_libraries(${MODULE_NAME} winpr)
+
+ set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+ if(NOT MSVC)
+ set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}Area")
+ else()
+ set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}Area")
+ endif()
+
+ file(MAKE_DIRECTORY "${TEST_AREA}")
+ file(WRITE "${TEST_AREA}/TestFile1" "TestFile1")
+ file(WRITE "${TEST_AREA}/TestFile2" "TestFile2")
+ file(WRITE "${TEST_AREA}/TestFile3" "TestFile3")
+ file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory1")
+ file(WRITE "${TEST_AREA}/TestDirectory1/TestDirectory1File1" "TestDirectory1File1")
+ file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory2")
+ file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File1" "TestDirectory2File1")
+ file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File2" "TestDirectory2File2")
+ file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory3")
+ file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File1" "TestDirectory3File1")
+ file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File2" "TestDirectory3File2")
+ file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File3" "TestDirectory3File3")
+
+ foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName} ${TEST_AREA})
+ endforeach()
+
+ set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
-set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
-
-set(${MODULE_PREFIX}_TESTS
- TestFileCreateFile.c
- TestFileDeleteFile.c
- TestFileReadFile.c
- TestFileWriteFile.c
- TestFilePatternMatch.c
- TestFileFindFirstFile.c
- TestFileFindFirstFileEx.c
- TestFileFindNextFile.c
- TestFileGetStdHandle.c
-)
-
-create_test_sourcelist(${MODULE_PREFIX}_SRCS
- ${${MODULE_PREFIX}_DRIVER}
- ${${MODULE_PREFIX}_TESTS})
-
-add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
-
-target_link_libraries(${MODULE_NAME} winpr)
-
-set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
-
-if(NOT MSVC)
- set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}Area")
-else()
- set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}Area")
endif()
-
-file(MAKE_DIRECTORY "${TEST_AREA}")
-file(WRITE "${TEST_AREA}/TestFile1" "TestFile1")
-file(WRITE "${TEST_AREA}/TestFile2" "TestFile2")
-file(WRITE "${TEST_AREA}/TestFile3" "TestFile3")
-file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory1")
-file(WRITE "${TEST_AREA}/TestDirectory1/TestDirectory1File1" "TestDirectory1File1")
-file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory2")
-file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File1" "TestDirectory2File1")
-file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File2" "TestDirectory2File2")
-file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory3")
-file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File1" "TestDirectory3File1")
-file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File2" "TestDirectory3File2")
-file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File3" "TestDirectory3File3")
-
-foreach(test ${${MODULE_PREFIX}_TESTS})
- get_filename_component(TestName ${test} NAME_WE)
- add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName} ${TEST_AREA})
-endforeach()
-
-set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
diff --git a/winpr/libwinpr/file/test/TestSetFileAttributes.c b/winpr/libwinpr/file/test/TestSetFileAttributes.c
new file mode 100644
index 000000000..e81c89c82
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestSetFileAttributes.c
@@ -0,0 +1,149 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+#include <winpr/handle.h>
+#include <winpr/windows.h>
+#include <winpr/sysinfo.h>
+
+static const DWORD allflags[] = {
+ 0,
+ FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_DIRECTORY,
+ FILE_ATTRIBUTE_ARCHIVE,
+ FILE_ATTRIBUTE_DEVICE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_TEMPORARY,
+ FILE_ATTRIBUTE_SPARSE_FILE,
+ FILE_ATTRIBUTE_REPARSE_POINT,
+ FILE_ATTRIBUTE_COMPRESSED,
+ FILE_ATTRIBUTE_OFFLINE,
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
+ FILE_ATTRIBUTE_ENCRYPTED,
+ FILE_ATTRIBUTE_VIRTUAL,
+ FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DEVICE |
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_OFFLINE,
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_VIRTUAL
+};
+
+static BOOL test_SetFileAttributesA(void)
+{
+ BOOL rc = FALSE;
+ HANDLE handle;
+ DWORD x;
+ const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY };
+ char* name = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd");
+ if (!name)
+ goto fail;
+
+ for (x = 0; x < ARRAYSIZE(allflags); x++)
+ {
+ const DWORD flag = allflags[x];
+ rc = SetFileAttributesA(NULL, flag);
+ if (rc)
+ goto fail;
+
+ rc = SetFileAttributesA(name, flag);
+ if (rc)
+ goto fail;
+ }
+
+ handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ goto fail;
+ CloseHandle(handle);
+
+ for (x = 0; x < ARRAYSIZE(flags); x++)
+ {
+ DWORD attr;
+ const DWORD flag = flags[x];
+ rc = SetFileAttributesA(name, flag);
+ if (!rc)
+ goto fail;
+
+ attr = GetFileAttributesA(name);
+ if (flag != 0)
+ {
+ if ((attr & flag) == 0)
+ goto fail;
+ }
+ }
+
+fail:
+ DeleteFileA(name);
+ free(name);
+ return rc;
+}
+
+static BOOL test_SetFileAttributesW(void)
+{
+ BOOL rc = FALSE;
+ WCHAR* name = NULL;
+ HANDLE handle;
+ DWORD x;
+ const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY };
+ char* base = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd");
+ if (!base)
+ goto fail;
+
+ ConvertToUnicode(CP_UTF8, 0, base, -1, &name, 0);
+
+ for (x = 0; x < ARRAYSIZE(allflags); x++)
+ {
+ const DWORD flag = allflags[x];
+ rc = SetFileAttributesW(NULL, flag);
+ if (rc)
+ goto fail;
+
+ rc = SetFileAttributesW(name, flag);
+ if (rc)
+ goto fail;
+ }
+
+ handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ goto fail;
+ CloseHandle(handle);
+
+ for (x = 0; x < ARRAYSIZE(flags); x++)
+ {
+ DWORD attr;
+ const DWORD flag = flags[x];
+ rc = SetFileAttributesW(name, flag);
+ if (!rc)
+ goto fail;
+
+ attr = GetFileAttributesW(name);
+ if (flag != 0)
+ {
+ if ((attr & flag) == 0)
+ goto fail;
+ }
+ }
+
+fail:
+ DeleteFileW(name);
+ free(name);
+ free(base);
+ return rc;
+}
+
+int TestSetFileAttributes(int argc, char* argv[])
+{
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!test_SetFileAttributesA())
+ return -1;
+ if (!test_SetFileAttributesW())
+ return -1;
+ return 0;
+}