From a1d8c01c1cabd115dda8c298941d1786fb4c5c2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Veronika=20Hanul=C3=ADkov=C3=A1?= <vhanulik@redhat.com>
Date: Wed, 17 Jul 2024 12:53:52 +0200
Subject: [PATCH] pkcs15-tcos: Check certificate length before accessing

Thanks Matteo Marini for report
https://github.com/OpenSC/OpenSC/security/advisories/GHSA-p3mx-7472-h3j8

fuzz_pkcs15_encode/8

CVE: CVE-2024-45619
Upstream-Status: Backport [https://github.com/OpenSC/OpenSC/commit/a1d8c01c1cabd115dda8c298941d1786fb4c5c2f]

Signed-off-by: Zhang Peng <peng.zhang1.cn@windriver.com>
---
 src/libopensc/pkcs15-tcos.c | 35 +++++++++++++++++++++--------------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/libopensc/pkcs15-tcos.c b/src/libopensc/pkcs15-tcos.c
index 2bd275c4f4..ecaa66edf2 100644
--- a/src/libopensc/pkcs15-tcos.c
+++ b/src/libopensc/pkcs15-tcos.c
@@ -45,6 +45,7 @@ static int insert_cert(
 	struct sc_pkcs15_cert_info cert_info;
 	struct sc_pkcs15_object cert_obj;
 	unsigned char cert[20];
+	size_t cert_len = 0;
 	int r;
 
 	memset(&cert_info, 0, sizeof(cert_info));
@@ -57,25 +58,31 @@ static int insert_cert(
 	strlcpy(cert_obj.label, label, sizeof(cert_obj.label));
 	cert_obj.flags = writable ? SC_PKCS15_CO_FLAG_MODIFIABLE : 0;
 
-	if(sc_select_file(card, &cert_info.path, NULL)!=SC_SUCCESS){
-		sc_log(ctx, 
-			"Select(%s) failed\n", path);
+	if (sc_select_file(card, &cert_info.path, NULL) != SC_SUCCESS) {
+		sc_log(ctx, "Select(%s) failed", path);
 		return 1;
 	}
 	r = sc_read_binary(card, 0, cert, sizeof(cert), 0);
-	if (r <= 0){
-		sc_log(ctx, 
-			"ReadBinary(%s) failed\n", path);
+	if (r <= 0) {
+		sc_log(ctx, "ReadBinary(%s) failed\n", path);
 		return 2;
 	}
-	if(cert[0]!=0x30 || cert[1]!=0x82){
-		sc_log(ctx, 
-			"Invalid Cert: %02X:%02X:...\n", cert[0], cert[1]);
+	cert_len = r; /* actual number of read bytes */
+	if (cert_len < 7 || (size_t)(7 + cert[5]) > cert_len) {
+		sc_log(ctx, "Invalid certificate length");
+		return 3;
+	}
+	if (cert[0] != 0x30 || cert[1] != 0x82) {
+		sc_log(ctx, "Invalid Cert: %02X:%02X:...\n", cert[0], cert[1]);
 		return 3;
 	}
 
 	/* some certificates are prefixed by an OID */
-	if(cert[4]==0x06 && cert[5]<10 && cert[6+cert[5]]==0x30 && cert[7+cert[5]]==0x82){
+	if (cert[4] == 0x06 && cert[5] < 10 && cert[6 + cert[5]] == 0x30 && cert[7 + cert[5]] == 0x82) {
+		if ((size_t)(9 + cert[5]) > cert_len) {
+			sc_log(ctx, "Invalid certificate length");
+			return 3;
+		}
 		cert_info.path.index=6+cert[5];
 		cert_info.path.count=(cert[8+cert[5]]<<8) + cert[9+cert[5]] + 4;
 	} else {
@@ -83,12 +90,12 @@ static int insert_cert(
 		cert_info.path.count=(cert[2]<<8) + cert[3] + 4;
 	}
 
-	r=sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
-	if(r!=SC_SUCCESS){
-		sc_log(ctx,  "sc_pkcs15emu_add_x509_cert(%s) failed\n", path);
+	r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
+	if (r != SC_SUCCESS) {
+		sc_log(ctx, "sc_pkcs15emu_add_x509_cert(%s) failed", path);
 		return 4;
 	}
-	sc_log(ctx,  "%s: OK, Index=%d, Count=%d\n", path, cert_info.path.index, cert_info.path.count);
+	sc_log(ctx, "%s: OK, Index=%d, Count=%d", path, cert_info.path.index, cert_info.path.count);
 	return 0;
 }

--
2.34.1
