Repository: FreeRDP/FreeRDP (C)

Issue:
Remote Assistance File Parsing
There appears to be an issue with remote assistance file parsing in the [current master](https://github.com/FreeRDP/FreeRDP/commit/33447dc16e8f9e52b2b46cccdcc017bad1908008). I tried generating .msrcIncident from a variety of different Windows versions (Windows 11, Windows 10, Windows 7) and all of the ones I tested currently don't work due to the same issue.

When [`freerdp_assistance_parse_all_elements_of_l`](https://github.com/FreeRDP/FreeRDP/blob/master/libfreerdp/common/assistance.c#L669) is done parsing, `file` contains 4 entries in `MachinePorts` but 8 entries in `MachineAddresses`.

This causes [`freerdp_settings_set_pointer_array`](https://github.com/FreeRDP/FreeRDP/blob/master/libfreerdp/common/assistance.c#L1326) to fail with an invalid offset later on when setting `FreeRDP_TargetNetAddresses`.

Here is an example .msrcIncident which can be used for reproduction. The password is `4X638PTVZTKZ`: https://gist.github.com/lemonmojo/6bef0edf563ad9631ceae8d3d29a2aef


Discussion: @akallabeth @mfleisz Unfortunately that now leads to an overfree in `freerdp_target_net_addresses_free`. At least in the case when the connections fails to establish.

<img width="1699" alt="Screenshot 2024-01-04 at 11 50 54" src="https://github.com/FreeRDP/FreeRDP/assets/186198/3bf9ae1e-05b7-4194-bd05-96fd980ddefe">



Fix (patch):
diff --git a/libfreerdp/common/assistance.c b/libfreerdp/common/assistance.c
index e00351516eeb..e714e633006d 100644
--- a/libfreerdp/common/assistance.c
+++ b/libfreerdp/common/assistance.c
@@ -37,6 +37,8 @@
 
 #include <freerdp/assistance.h>
 
+#include "../core/settings.h"
+
 #define TAG FREERDP_TAG("common")
 
 struct rdp_assistance_file
@@ -1314,21 +1316,27 @@ BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile
 	if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE))
 		return FALSE;
 
-	size_t ports = ArrayList_Count(file->MachinePorts);
+	const size_t ports = ArrayList_Count(file->MachinePorts);
+	const size_t addresses = ArrayList_Count(file->MachineAddresses);
 	if (ports < 1)
 		return FALSE;
+	if (ports != addresses)
+		return FALSE;
 
 	const UINT32 port = (UINT32)ArrayList_GetItem(file->MachinePorts, 0);
 	if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, port))
 		return FALSE;
 
-	if (!freerdp_settings_set_pointer_len(settings, FreeRDP_TargetNetAddresses, NULL, ports))
-		return FALSE;
-	if (!freerdp_settings_set_pointer_len(settings, FreeRDP_TargetNetPorts, file->MachinePorts,
-	                                      ports))
+	if (!freerdp_target_net_adresses_reset(settings, ports))
 		return FALSE;
 
-	for (size_t i = 0; i < ArrayList_Count(file->MachineAddresses); i++)
+	for (size_t x = 0; x < ports; x++)
+	{
+		const UINT32 port = (UINT32)ArrayList_GetItem(file->MachinePorts, x);
+		if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port))
+			return FALSE;
+	}
+	for (size_t i = 0; i < addresses; i++)
 	{
 		const char* maddr = ArrayList_GetItem(file->MachineAddresses, i);
 		if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr))
diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c
index 0733de39f5b3..29efeacdbb0f 100644
--- a/libfreerdp/common/settings.c
+++ b/libfreerdp/common/settings.c
@@ -1243,9 +1243,8 @@ BOOL freerdp_settings_set_value_for_name(rdpSettings* settings, const char* name
 	return FALSE;
 }
 
-static BOOL freerdp_settings_set_pointer_len_(rdpSettings* settings,
-                                              FreeRDP_Settings_Keys_Pointer id, SSIZE_T lenId,
-                                              const void* data, size_t len, size_t size)
+BOOL freerdp_settings_set_pointer_len_(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id,
+                                       SSIZE_T lenId, const void* data, size_t len, size_t size)
 {
 	BOOL rc = FALSE;
 	void* copy = NULL;
@@ -1373,11 +1372,15 @@ BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, FreeRDP_Settings_Ke
 			return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_ServerCertificateLength,
 			                                         data, len, sizeof(char));
 		case FreeRDP_TargetNetAddresses:
-			if (data == NULL)
+			if ((data == NULL) && (len == 0))
+			{
 				freerdp_target_net_addresses_free(settings);
-			return freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetAddresses,
-			                                         FreeRDP_TargetNetAddressCount, data, len,
-			                                         sizeof(char*));
+				return TRUE;
+			}
+			WLog_WARN(
+			    TAG,
+			    "[BUG] FreeRDP_TargetNetAddresses must not be resized from outside the library!");
+			return FALSE;
 		case FreeRDP_ServerLicenseProductIssuers:
 			if (data == NULL)
 				freerdp_server_license_issuers_free(settings);
@@ -1385,11 +1388,14 @@ BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, FreeRDP_Settings_Ke
 			                                         FreeRDP_ServerLicenseProductIssuersCount, data,
 			                                         len, sizeof(char*));
 		case FreeRDP_TargetNetPorts:
-			if (data == NULL)
+			if ((data == NULL) && (len == 0))
+			{
 				freerdp_target_net_addresses_free(settings);
-			return freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetPorts,
-			                                         FreeRDP_TargetNetAddressCount, data, len,
-			                                         sizeof(UINT32));
+				return TRUE;
+			}
+			WLog_WARN(TAG,
+			          "[BUG] FreeRDP_TargetNetPorts must not be resized from outside the library!");
+			return FALSE;
 		case FreeRDP_DeviceArray:
 			if (data == NULL)
 				freerdp_device_collection_free(settings);
@@ -1826,27 +1832,16 @@ ADDIN_ARGV* freerdp_dynamic_channel_clone(ADDIN_ARGV* channel)
 
 BOOL freerdp_target_net_addresses_copy(rdpSettings* settings, char** addresses, UINT32 count)
 {
-	UINT32 i;
-
 	WINPR_ASSERT(settings);
 	WINPR_ASSERT(addresses);
 
-	freerdp_target_net_addresses_free(settings);
-
-	settings->TargetNetAddressCount = count;
-	settings->TargetNetAddresses = (char**)calloc(settings->TargetNetAddressCount, sizeof(char*));
-
-	if (!settings->TargetNetAddresses)
-	{
-		freerdp_target_net_addresses_free(settings);
+	if (!freerdp_target_net_adresses_reset(settings, count))
 		return FALSE;
-	}
 
-	for (i = 0; i < settings->TargetNetAddressCount; i++)
+	for (UINT32 i = 0; i < settings->TargetNetAddressCount; i++)
 	{
-		settings->TargetNetAddresses[i] = _strdup(addresses[i]);
-
-		if (!settings->TargetNetAddresses[i])
+		if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i,
+		                                        addresses[i]))
 		{
 			freerdp_target_net_addresses_free(settings);
 			return FALSE;
diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c
index 8ba12bfed21a..db6bc13fc8e1 100644
--- a/libfreerdp/core/settings.c
+++ b/libfreerdp/core/settings.c
@@ -981,47 +981,20 @@ static BOOL freerdp_settings_int_buffer_copy(rdpSettings* _settings, const rdpSe
 	                                      RedirectionTsvUrlLength))
 		goto out_fail;
 
-	freerdp_settings_set_uint32(
-	    _settings, FreeRDP_TargetNetAddressCount,
-	    freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount));
+	const UINT32 nrports = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount);
+	if (!freerdp_target_net_adresses_reset(_settings, nrports))
+		return FALSE;
 
-	if (freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount) > 0)
+	for (UINT32 i = 0; i < nrports; i++)
 	{
-		_settings->TargetNetAddresses = (char**)calloc(
-		    freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount), sizeof(char*));
-
-		if (!_settings->TargetNetAddresses)
-		{
-			freerdp_settings_set_uint32(_settings, FreeRDP_TargetNetAddressCount, 0);
-			goto out_fail;
-		}
-
-		for (index = 0;
-		     index < freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); index++)
-		{
-			_settings->TargetNetAddresses[index] = _strdup(settings->TargetNetAddresses[index]);
-
-			if (!_settings->TargetNetAddresses[index])
-			{
-				while (index)
-					free(_settings->TargetNetAddresses[--index]);
-
-				free(_settings->TargetNetAddresses);
-				_settings->TargetNetAddresses = NULL;
-				freerdp_settings_set_uint32(_settings, FreeRDP_TargetNetAddressCount, 0);
-				goto out_fail;
-			}
-		}
-
-		const void* ports = freerdp_settings_get_pointer(settings, FreeRDP_TargetNetPorts);
-		if (ports)
-		{
-			const UINT32 nrports =
-			    freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount);
-			if (!freerdp_settings_set_pointer_len(_settings, FreeRDP_TargetNetPorts, ports,
-			                                      nrports))
-				goto out_fail;
-		}
+		const char* address =
+		    freerdp_settings_get_pointer_array(settings, FreeRDP_TargetNetAddresses, i);
+		const UINT32* port =
+		    freerdp_settings_get_pointer_array(settings, FreeRDP_TargetNetPorts, i);
+		if (!freerdp_settings_set_pointer_array(_settings, FreeRDP_TargetNetAddresses, i, address))
+			return FALSE;
+		if (!freerdp_settings_set_pointer_array(_settings, FreeRDP_TargetNetPorts, i, port))
+			return FALSE;
 	}
 
 	{
@@ -1123,6 +1096,7 @@ BOOL freerdp_settings_copy(rdpSettings* _settings, const rdpSettings* settings)
 	_settings->RedirectionPassword = NULL;
 	_settings->RedirectionTsvUrl = NULL;
 	_settings->TargetNetAddresses = NULL;
+	_settings->TargetNetPorts = NULL;
 	_settings->DeviceArray = NULL;
 	_settings->StaticChannelArray = NULL;
 	_settings->DynamicChannelArray = NULL;
@@ -1269,3 +1243,20 @@ const char* freerdp_settings_glyph_level_string(UINT32 level, char* buffer, size
 	_snprintf(buffer, size, "%s[0x%08" PRIx32 "]", str, level);
 	return buffer;
 }
+
+BOOL freerdp_target_net_adresses_reset(rdpSettings* settings, size_t size)
+{
+	freerdp_target_net_addresses_free(settings);
+
+	if (size > 0)
+	{
+		if (!freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetPorts, -1, NULL, size,
+		                                       sizeof(UINT32)))
+			return FALSE;
+		if (!freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetAddresses,
+		                                       FreeRDP_TargetNetAddressCount, NULL, size,
+		                                       sizeof(char*)))
+			return FALSE;
+	}
+	return TRUE;
+}
diff --git a/libfreerdp/core/settings.h b/libfreerdp/core/settings.h
index 062eca6f3896..270925a899e3 100644
--- a/libfreerdp/core/settings.h
+++ b/libfreerdp/core/settings.h
@@ -66,4 +66,10 @@ FREERDP_LOCAL BOOL identity_set_from_smartcard_hash(SEC_WINNT_AUTH_IDENTITY_W* i
 FREERDP_LOCAL const char* freerdp_settings_glyph_level_string(UINT32 level, char* buffer,
                                                               size_t size);
 
+FREERDP_LOCAL BOOL freerdp_settings_set_pointer_len_(rdpSettings* settings,
+                                                     FreeRDP_Settings_Keys_Pointer id,
+                                                     SSIZE_T lenId, const void* data, size_t len,
+                                                     size_t size);
+FREERDP_LOCAL BOOL freerdp_target_net_adresses_reset(rdpSettings* settings, size_t size);
+
 #endif /* FREERDP_LIB_CORE_SETTINGS_H */