Vulnerability: An issue was discovered in OpenSC through 0.19.0 and 0.20.x through 0.20.0-rc3. libopensc/card-cac1.c mishandles buffer limits for CAC certificates.
Vulnerability ID: CVE-2019-19481
CWE: CWE-119
Severity score: 2.9
Project: https://github.com/OpenSC/OpenSC
Published: 2019-12-01
File: src/libopensc/card-cac1.c
Commit message: cac1: Correctly handle the buffer limits

Found by oss-fuzz

https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18618
and others
Commit author: Jakub Jelen

Vulnerable version (before fix):
 static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_l 	u8 *out_ptr; 	size_t size = 0; 	size_t left = 0; 	size_t len, next_len; 	sc_apdu_t apdu; 	int r = SC_SUCCESS; 	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 	/* get the size */ 	size = left = *out_buf ? *out_len : sizeof(buf); 	out_ptr = *out_buf ? *out_buf : buf; 	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 ); 	next_len = MIN(left, 100); 	for (; left > 0; left -= len, out_ptr += len) { 		len = next_len; 		apdu.resp = out_ptr; 		apdu.le = len; 		apdu.resplen = left; static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_l 			left -= len; 			break; 		} 		next_len = MIN(left, apdu.sw2); 	} 	if (r < 0) { 		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); static int cac_read_binary(sc_card_t *card, unsigned int idx, 	int r = 0; 	u8 *val = NULL; 	u8 *cert_ptr; 	size_t val_len; 	size_t len, cert_len; 	u8 cert_type; 

Fixed version (after fix):
 static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_l 	u8 *out_ptr; 	size_t size = 0; 	size_t left = 0; 	size_t len; 	sc_apdu_t apdu; 	int r = SC_SUCCESS; 	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 	/* get the size */ 	size = left = *out_buf ? *out_len : sizeof(buf); 	out_ptr = *out_buf ? *out_buf : buf; 	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 ); 	len = MIN(left, 100); 	for (; left > 0;) { /* Increments for readability in the end of the function */ 		apdu.resp = out_ptr; 		apdu.le = len; 		apdu.resplen = left; static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_l 			left -= len; 			break; 		} 		/* Adjust the lengths */ 		left -= len; 		out_ptr += len; 		len = MIN(left, apdu.sw2); 	} 	if (r < 0) { 		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); static int cac_read_binary(sc_card_t *card, unsigned int idx, 	int r = 0; 	u8 *val = NULL; 	u8 *cert_ptr; 	size_t val_len = 0; 	size_t len, cert_len; 	u8 cert_type; 

Patch:
patch: @@ -69,17 +69,16 @@ static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_l
 	u8 *out_ptr;
 	size_t size = 0;
 	size_t left = 0;
-	size_t len, next_len;
+	size_t len;
 	sc_apdu_t apdu;
 	int r = SC_SUCCESS;
 	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
 	/* get the size */
 	size = left = *out_buf ? *out_len : sizeof(buf);
 	out_ptr = *out_buf ? *out_buf : buf;
 	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 );
-	next_len = MIN(left, 100);
-	for (; left > 0; left -= len, out_ptr += len) {
-		len = next_len;
+	len = MIN(left, 100);
+	for (; left > 0;) { /* Increments for readability in the end of the function */
 		apdu.resp = out_ptr;
 		apdu.le = len;
 		apdu.resplen = left;
@@ -98,7 +97,10 @@ static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_l
 			left -= len;
 			break;
 		}
-		next_len = MIN(left, apdu.sw2);
+		/* Adjust the lengths */
+		left -= len;
+		out_ptr += len;
+		len = MIN(left, apdu.sw2);
 	}
 	if (r < 0) {
 		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
@@ -128,7 +130,7 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx,
 	int r = 0;
 	u8 *val = NULL;
 	u8 *cert_ptr;
-	size_t val_len;
+	size_t val_len = 0;
 	size_t len, cert_len;
 	u8 cert_type;
path: src/libopensc/card-cac1.c