CVE-2020-11089 (published 2020-05-29T20:15Z)

In FreeRDP before 2.1.0, there is an out-of-bound read in irp functions (parallel_process_irp_create, serial_process_irp_create, drive_process_irp_write, printer_process_irp_write, rdpei_recv_pdu, serial_process_irp_write). This has been fixed in 2.1.0.

Severity: MEDIUM
CVSS v2 base score: 6.0 (AV:N/AC:M/Au:S/C:P/I:P/A:P)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L)

Weakness information:
cwe_id: CWE-125
cwe_name: Out-of-bounds Read
description: The product reads data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/FreeRDP/FreeRDP
commit_details: repo_url: https://github.com/FreeRDP/FreeRDP
author: akallabeth
author_date: 2020-04-15 17:09:22+02:00
msg: Fixed oob read in parallel_process_irp_create
repository: repo_url: https://github.com/FreeRDP/FreeRDP
repo_name: FreeRDP/FreeRDP
description: FreeRDP is a free remote desktop protocol library and clients
repo_language: C
owner: FreeRDP
file_changes: filename: parallel_main.c
change_type: ModificationType.MODIFY
diff: @@ -83,13 +83,19 @@ static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
 {
 	char* path = NULL;
 	int status;
+	WCHAR* ptr;
 	UINT32 PathLength;
-	Stream_Seek(irp->input, 28);
+	if (!Stream_SafeSeek(irp->input, 28))
+		return ERROR_INVALID_DATA;
 	/* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
 	/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
+	if (Stream_GetRemainingLength(irp->input) < 4)
+		return ERROR_INVALID_DATA;
 	Stream_Read_UINT32(irp->input, PathLength);
-	status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)Stream_Pointer(irp->input), PathLength / 2,
-	                            &path, 0, NULL, NULL);
+	ptr = (WCHAR*)Stream_Pointer(irp->input);
+	if (!Stream_SafeSeek(irp->input, PathLength))
+		return ERROR_INVALID_DATA;
+	status = ConvertFromUnicode(CP_UTF8, 0, ptr, PathLength / 2, &path, 0, NULL, NULL);
 
 	if (status < 1)
 		if (!(path = (char*)calloc(1, 1)))

programming_language: C
repo_url: https://github.com/FreeRDP/FreeRDP
commit_details: repo_url: https://github.com/FreeRDP/FreeRDP
author: akallabeth
author_date: 2020-04-15 17:21:46+02:00
msg: Fixed oob read in irp_write and similar
repository: repo_url: https://github.com/FreeRDP/FreeRDP
repo_name: FreeRDP/FreeRDP
description: FreeRDP is a free remote desktop protocol library and clients
repo_language: C
owner: FreeRDP
file_changes: filename: drive_main.c
change_type: ModificationType.MODIFY
diff: @@ -331,6 +331,7 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
 	DRIVE_FILE* file;
 	UINT32 Length;
 	UINT64 Offset;
+	void* ptr;
 
 	if (!drive || !irp || !irp->input || !irp->output || !irp->Complete)
 		return ERROR_INVALID_PARAMETER;
@@ -341,6 +342,9 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
 	Stream_Read_UINT32(irp->input, Length);
 	Stream_Read_UINT64(irp->input, Offset);
 	Stream_Seek(irp->input, 20); /* Padding */
+	ptr = Stream_Pointer(irp->input);
+	if (!Stream_SafeSeek(irp->input, Length))
+		return ERROR_INVALID_DATA;
 	file = drive_get_file_by_id(drive, irp->FileId);
 
 	if (!file)
@@ -353,7 +357,7 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
 		irp->IoStatus = drive_map_windows_err(GetLastError());
 		Length = 0;
 	}
-	else if (!drive_file_write(file, Stream_Pointer(irp->input), Length))
+	else if (!drive_file_write(file, ptr, Length))
 	{
 		irp->IoStatus = drive_map_windows_err(GetLastError());
 		Length = 0;

programming_language: C
filename: printer_main.c
change_type: ModificationType.MODIFY
diff: @@ -478,10 +478,16 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
 	UINT64 Offset;
 	rdpPrintJob* printjob = NULL;
 	UINT error = CHANNEL_RC_OK;
+	void* ptr;
+
+	if (Stream_GetRemainingLength(irp->input) < 32)
+		return ERROR_INVALID_DATA;
 	Stream_Read_UINT32(irp->input, Length);
 	Stream_Read_UINT64(irp->input, Offset);
 	Stream_Seek(irp->input, 20); /* Padding */
-
+	ptr = Stream_Pointer(irp->input);
+	if (!Stream_SafeSeek(irp->input, Length))
+		return ERROR_INVALID_DATA;
 	if (printer_dev->printer)
 		printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
 
@@ -492,7 +498,7 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
 	}
 	else
 	{
-		error = printjob->Write(printjob, Stream_Pointer(irp->input), Length);
+		error = printjob->Write(printjob, ptr, Length);
 	}
 
 	if (error)

programming_language: C++
filename: rdpei_main.c
change_type: ModificationType.MODIFY
diff: @@ -445,6 +445,9 @@ static UINT rdpei_recv_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
 	UINT16 eventId;
 	UINT32 pduLength;
 	UINT error;
+	if (Stream_GetRemainingLength(s) < 6)
+		return ERROR_INVALID_DATA;
+
 	Stream_Read_UINT16(s, eventId);   /* eventId (2 bytes) */
 	Stream_Read_UINT32(s, pduLength); /* pduLength (4 bytes) */
 #ifdef WITH_DEBUG_RDPEI

programming_language: C
filename: serial_main.c
change_type: ModificationType.MODIFY
diff: @@ -142,10 +142,9 @@ static UINT serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
 	Stream_Seek_UINT32(irp->input);                    /* CreateOptions (4 bytes) */
 	Stream_Read_UINT32(irp->input, PathLength);        /* PathLength (4 bytes) */
 
-	if (Stream_GetRemainingLength(irp->input) < PathLength)
+	if (!Stream_SafeSeek(irp->input, PathLength)) /* Path (variable) */
 		return ERROR_INVALID_DATA;
 
-	Stream_Seek(irp->input, PathLength); /* Path (variable) */
 	assert(PathLength == 0);             /* MS-RDPESP 2.2.2.2 */
 #ifndef _WIN32
 	/* Windows 2012 server sends on a first call :
@@ -300,6 +299,7 @@ static UINT serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
 {
 	UINT32 Length;
 	UINT64 Offset;
+	void* ptr;
 	DWORD nbWritten = 0;
 
 	if (Stream_GetRemainingLength(irp->input) < 32)
@@ -307,7 +307,9 @@ static UINT serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
 
 	Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
 	Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
-	Stream_Seek(irp->input, 20);            /* Padding (20 bytes) */
+	if (!Stream_SafeSeek(irp->input, 20))   /* Padding (20 bytes) */
+		return ERROR_INVALID_DATA;
+
 	/* MS-RDPESP 3.2.5.1.5: The Offset field is ignored
 	 * assert(Offset == 0);
 	 *
@@ -317,8 +319,11 @@ static UINT serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
 	WLog_Print(serial->log, WLOG_DEBUG, "writing %" PRIu32 " bytes to %s", Length,
 	           serial->device.name);
 
+	ptr = Stream_Pointer(irp->input);
+	if (!Stream_SafeSeek(irp->input, Length))
+		return ERROR_INVALID_DATA;
 	/* FIXME: CommWriteFile to be replaced by WriteFile */
-	if (CommWriteFile(serial->hComm, Stream_Pointer(irp->input), Length, &nbWritten, NULL))
+	if (CommWriteFile(serial->hComm, ptr, Length, &nbWritten, NULL))
 	{
 		irp->IoStatus = STATUS_SUCCESS;
 	}

programming_language: C
filename: rdg.c
change_type: ModificationType.MODIFY
diff: @@ -304,7 +304,8 @@ static wStream* rdg_receive_packet(rdpRdg* rdg)
 	Stream_Seek(s, 4);
 	Stream_Read_UINT32(s, packetLength);
 
-	if ((packetLength > INT_MAX) || !Stream_EnsureCapacity(s, packetLength))
+	if ((packetLength > INT_MAX) || !Stream_EnsureCapacity(s, packetLength) ||
+	    (packetLength < header))
 	{
 		Stream_Free(s, TRUE);
 		return NULL;

programming_language: C
filename: nego.c
change_type: ModificationType.MODIFY
diff: @@ -91,9 +91,9 @@ static BOOL nego_security_connect(rdpNego* nego);
 static BOOL nego_send_preconnection_pdu(rdpNego* nego);
 static BOOL nego_recv_response(rdpNego* nego);
 static void nego_send(rdpNego* nego);
-static void nego_process_negotiation_request(rdpNego* nego, wStream* s);
-static void nego_process_negotiation_response(rdpNego* nego, wStream* s);
-static void nego_process_negotiation_failure(rdpNego* nego, wStream* s);
+static BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s);
+static BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s);
+static BOOL nego_process_negotiation_failure(rdpNego* nego, wStream* s);
 
 /**
  * Negotiate protocol security and connect.
@@ -618,7 +618,8 @@ int nego_recv(rdpTransport* transport, wStream* s, void* extra)
 		switch (type)
 		{
 			case TYPE_RDP_NEG_RSP:
-				nego_process_negotiation_response(nego, s);
+				if (!nego_process_negotiation_response(nego, s))
+					return -1;
 				WLog_DBG(TAG, "selected_protocol: %" PRIu32 "", nego->SelectedProtocol);
 
 				/* enhanced security selected ? */
@@ -645,7 +646,8 @@ int nego_recv(rdpTransport* transport, wStream* s, void* extra)
 				break;
 
 			case TYPE_RDP_NEG_FAILURE:
-				nego_process_negotiation_failure(nego, s);
+				if (!nego_process_negotiation_failure(nego, s))
+					return -1;
 				break;
 		}
 	}
@@ -795,7 +797,8 @@ BOOL nego_read_request(rdpNego* nego, wStream* s)
 			return FALSE;
 		}
 
-		nego_process_negotiation_request(nego, s);
+		if (!nego_process_negotiation_request(nego, s))
+			return FALSE;
 	}
 
 	return tpkt_ensure_stream_consumed(s, length);
@@ -918,15 +921,19 @@ fail:
  * @param s
  */
 
-void nego_process_negotiation_request(rdpNego* nego, wStream* s)
+BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s)
 {
 	BYTE flags;
 	UINT16 length;
+
+	if (Stream_GetRemainingLength(s) < 7)
+		return FALSE;
 	Stream_Read_UINT8(s, flags);
 	Stream_Read_UINT16(s, length);
 	Stream_Read_UINT32(s, nego->RequestedProtocols);
 	WLog_DBG(TAG, "RDP_NEG_REQ: RequestedProtocol: 0x%08" PRIX32 "", nego->RequestedProtocols);
 	nego->state = NEGO_STATE_FINAL;
+	return TRUE;
 }
 
 /**
@@ -935,7 +942,7 @@ void nego_process_negotiation_request(rdpNego* nego, wStream* s)
  * @param s
  */
 
-void nego_process_negotiation_response(rdpNego* nego, wStream* s)
+BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s)
 {
 	UINT16 length;
 	WLog_DBG(TAG, "RDP_NEG_RSP");
@@ -944,13 +951,14 @@ void nego_process_negotiation_response(rdpNego* nego, wStream* s)
 	{
 		WLog_ERR(TAG, "Invalid RDP_NEG_RSP");
 		nego->state = NEGO_STATE_FAIL;
-		return;
+		return FALSE;
 	}
 
 	Stream_Read_UINT8(s, nego->flags);
 	Stream_Read_UINT16(s, length);
 	Stream_Read_UINT32(s, nego->SelectedProtocol);
 	nego->state = NEGO_STATE_FINAL;
+	return TRUE;
 }
 
 /**
@@ -959,12 +967,14 @@ void nego_process_negotiation_response(rdpNego* nego, wStream* s)
  * @param s
  */
 
-void nego_process_negotiation_failure(rdpNego* nego, wStream* s)
+BOOL nego_process_negotiation_failure(rdpNego* nego, wStream* s)
 {
 	BYTE flags;
 	UINT16 length;
 	UINT32 failureCode;
 	WLog_DBG(TAG, "RDP_NEG_FAILURE");
+	if (Stream_GetRemainingLength(s) < 7)
+		return FALSE;
 	Stream_Read_UINT8(s, flags);
 	Stream_Read_UINT16(s, length);
 	Stream_Read_UINT32(s, failureCode);
@@ -999,6 +1009,7 @@ void nego_process_negotiation_failure(rdpNego* nego, wStream* s)
 	}
 
 	nego->state = NEGO_STATE_FAIL;
+	return TRUE;
 }
 
 /**

programming_language: C++