From 8fe377e93b4b56060e5bbfb6f3142ceaeca744fa Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Fri, 25 May 2018 14:54:47 +0200 Subject: [PATCH] fixed out of bounds reads Thanks to Eric Sesterhenn from X41 D-SEC GmbH for reporting and suggesting security fixes. --- src/libopensc/asn1.c | 1 + src/libopensc/card-asepcos.c | 2 +- src/libopensc/card-authentic.c | 5 +++- src/libopensc/card-cac.c | 9 +++++-- src/libopensc/card-coolkey.c | 2 +- src/libopensc/card-entersafe.c | 8 +++--- src/libopensc/card-epass2003.c | 47 ++++++++++++++++++---------------- src/libopensc/card-gpk.c | 3 +++ src/libopensc/card-iasecc.c | 2 +- src/libopensc/card-oberthur.c | 7 +++++ src/libopensc/card-openpgp.c | 6 +++++ src/libopensc/card-piv.c | 9 ++++--- src/libopensc/card-rtecp.c | 2 +- src/libopensc/card-setcos.c | 18 +++++++++++-- src/libopensc/ef-gdo.c | 2 +- src/libopensc/pkcs15-itacns.c | 1 + src/libopensc/pkcs15-tcos.c | 8 +++--- src/tools/opensc-tool.c | 19 +++++++------- 18 files changed, 100 insertions(+), 51 deletions(-) diff --git a/src/libopensc/asn1.c b/src/libopensc/asn1.c index b1be88dc..e43c0f13 100644 --- a/src/libopensc/asn1.c +++ b/src/libopensc/asn1.c @@ -103,6 +103,7 @@ int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out, len = *p & 0x7f; if (*p++ & 0x80) { unsigned int a = 0; + left--; if (len > 4 || len > left) return SC_ERROR_INVALID_ASN1_OBJECT; left -= len; diff --git a/src/libopensc/card-asepcos.c b/src/libopensc/card-asepcos.c index 8055073c..5459d98f 100644 --- a/src/libopensc/card-asepcos.c +++ b/src/libopensc/card-asepcos.c @@ -169,7 +169,7 @@ static int asepcos_parse_sec_attr(sc_card_t *card, sc_file_t *file, const u8 *bu while (len != 0) { unsigned int amode, tlen = 3; - if (len < 5 && p[0] != 0x80 && p[1] != 0x01) { + if (len < 5 || p[0] != 0x80 || p[1] != 0x01) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid access mode encoding"); return SC_ERROR_INTERNAL; } diff --git a/src/libopensc/card-authentic.c b/src/libopensc/card-authentic.c index f72c2d20..9fbda94a 100644 --- a/src/libopensc/card-authentic.c +++ b/src/libopensc/card-authentic.c @@ -560,6 +560,9 @@ authentic_set_current_files(struct sc_card *card, struct sc_path *path, sc_file_dup(&card->cache.current_df, file); if (cur_df_path.len) { + if (cur_df_path.len + card->cache.current_df->path.len > sizeof card->cache.current_df->path.value + || cur_df_path.len > sizeof card->cache.current_df->path.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(card->cache.current_df->path.value + cur_df_path.len, card->cache.current_df->path.value, card->cache.current_df->path.len); @@ -988,7 +991,7 @@ authentic_process_fci(struct sc_card *card, struct sc_file *file, } sc_log_hex(ctx, "ACL data", file->sec_attr, file->sec_attr_len); - for (ii = 0; ii < file->sec_attr_len / 2; ii++) { + for (ii = 0; ii < file->sec_attr_len / 2 && ii < sizeof ops_DF; ii++) { unsigned char op = file->type == SC_FILE_TYPE_DF ? ops_DF[ii] : ops_EF[ii]; unsigned char acl = *(file->sec_attr + ii*2); unsigned char cred_id = *(file->sec_attr + ii*2 + 1); diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c index 4e853e5c..a30a0438 100644 --- a/src/libopensc/card-cac.c +++ b/src/libopensc/card-cac.c @@ -664,12 +664,17 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, cert_len = 0; cert_ptr = NULL; cert_type = 0; - for (tl_ptr = tl, val_ptr=val; tl_len >= 2; - val_len -= len, val_ptr += len, tl_len -= tl_head_len) { + for (tl_ptr = tl, val_ptr = val; tl_len >= 2; + val_len -= len, val_ptr += len, tl_len -= tl_head_len) { tl_start = tl_ptr; if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS) break; tl_head_len = tl_ptr - tl_start; + + /* incomplete value */ + if (val_len < len) + break; + if (tag == CAC_TAG_CERTIFICATE) { cert_len = len; cert_ptr = val_ptr; diff --git a/src/libopensc/card-coolkey.c b/src/libopensc/card-coolkey.c index 4c966f64..3c57628b 100644 --- a/src/libopensc/card-coolkey.c +++ b/src/libopensc/card-coolkey.c @@ -1467,7 +1467,7 @@ coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribut for (i=0; i < attribute_count; i++) { size_t record_len = coolkey_get_attribute_record_len(attr, object_record_type, buf_len); /* make sure we have the complete record */ - if (buf_len < record_len) { + if (buf_len < record_len || record_len < 4) { return SC_ERROR_CORRUPTED_DATA; } /* does the attribute match the one we are looking for */ diff --git a/src/libopensc/card-entersafe.c b/src/libopensc/card-entersafe.c index d5264349..828b0305 100644 --- a/src/libopensc/card-entersafe.c +++ b/src/libopensc/card-entersafe.c @@ -1408,13 +1408,15 @@ static int entersafe_gen_key(sc_card_t *card, sc_entersafe_gen_key_data *data) data->modulus = malloc(len); if (!data->modulus) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_OUT_OF_MEMORY); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY); p=rbuf; - assert(*p=='E'); + if (*p!='E') + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); p+=2+p[1]; /* N */ - assert(*p=='N'); + if (*p!='N') + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); ++p; if(*p++>0x80) { diff --git a/src/libopensc/card-epass2003.c b/src/libopensc/card-epass2003.c index 88fe8275..cbe417cf 100644 --- a/src/libopensc/card-epass2003.c +++ b/src/libopensc/card-epass2003.c @@ -740,11 +740,11 @@ construct_mac_tlv(struct sc_card *card, unsigned char *apdu_buf, size_t data_tlv memcpy(mac_tlv + 2, &mac[mac_len - 16], 8); } else { - unsigned char iv[8] = { 0 }; + unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; unsigned char tmp[8] = { 0 }; des_encrypt_cbc(exdata->sk_mac, 8, icv, apdu_buf, mac_len, mac); des_decrypt_cbc(&exdata->sk_mac[8], 8, iv, &mac[mac_len - 8], 8, tmp); - memset(iv, 0x00, 8); + memset(iv, 0x00, sizeof iv); des_encrypt_cbc(exdata->sk_mac, 8, iv, tmp, 8, mac_tlv + 2); } @@ -903,9 +903,9 @@ epass2003_sm_wrap_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_ap * SW12(TLV)=0x99|0x02|SW1+SW2 * MAC(TLV)=0x8e|0x08|MAC */ static int -decrypt_response(struct sc_card *card, unsigned char *in, unsigned char *out, size_t * out_len) +decrypt_response(struct sc_card *card, unsigned char *in, size_t inlen, unsigned char *out, size_t * out_len) { - size_t in_len; + size_t cipher_len; size_t i; unsigned char iv[16] = { 0 }; unsigned char plaintext[4096] = { 0 }; @@ -922,37 +922,40 @@ decrypt_response(struct sc_card *card, unsigned char *in, unsigned char *out, si /* parse cipher length */ if (0x01 == in[2] && 0x82 != in[1]) { - in_len = in[1]; + cipher_len = in[1]; i = 3; } else if (0x01 == in[3] && 0x81 == in[1]) { - in_len = in[2]; + cipher_len = in[2]; i = 4; } else if (0x01 == in[4] && 0x82 == in[1]) { - in_len = in[2] * 0x100; - in_len += in[3]; + cipher_len = in[2] * 0x100; + cipher_len += in[3]; i = 5; } else { return -1; } - /* decrypt */ - if (KEY_TYPE_AES == exdata->smtype) - aes128_decrypt_cbc(exdata->sk_enc, 16, iv, &in[i], in_len - 1, plaintext); - else - des3_decrypt_cbc(exdata->sk_enc, 16, iv, &in[i], in_len - 1, plaintext); - - /* unpadding */ - while (0x80 != plaintext[in_len - 2] && (in_len - 2 > 0)) - in_len--; - - if (2 == in_len) + if (cipher_len < 2 || i+cipher_len > inlen || cipher_len > sizeof plaintext) return -1; - memcpy(out, plaintext, in_len - 2); - *out_len = in_len - 2; + /* decrypt */ + if (KEY_TYPE_AES == exdata->smtype) + aes128_decrypt_cbc(exdata->sk_enc, 16, iv, &in[i], cipher_len - 1, plaintext); + else + des3_decrypt_cbc(exdata->sk_enc, 16, iv, &in[i], cipher_len - 1, plaintext); + + /* unpadding */ + while (0x80 != plaintext[cipher_len - 2] && (cipher_len - 2 > 0)) + cipher_len--; + + if (2 == cipher_len) + return -1; + + memcpy(out, plaintext, cipher_len - 2); + *out_len = cipher_len - 2; return 0; } @@ -974,7 +977,7 @@ epass2003_sm_unwrap_apdu(struct sc_card *card, struct sc_apdu *sm, struct sc_apd r = sc_check_sw(card, sm->sw1, sm->sw2); if (r == SC_SUCCESS) { if (exdata->sm) { - if (0 != decrypt_response(card, sm->resp, plain->resp, &len)) + if (0 != decrypt_response(card, sm->resp, sm->resplen, plain->resp, &len)) return SC_ERROR_CARD_CMD_FAILED; } else { diff --git a/src/libopensc/card-gpk.c b/src/libopensc/card-gpk.c index b3dd848c..c8d44691 100644 --- a/src/libopensc/card-gpk.c +++ b/src/libopensc/card-gpk.c @@ -409,6 +409,9 @@ gpk_parse_fileinfo(sc_card_t *card, if (sp[0] == 0x85) { unsigned int ac[3], n; + if (sp + 11 + 2*3 >= end) + break; + file->id = (sp[4] << 8) | sp[5]; file->size = (sp[8] << 8) | sp[9]; file->record_length = sp[7]; diff --git a/src/libopensc/card-iasecc.c b/src/libopensc/card-iasecc.c index 3e0d3acd..48d4cea5 100644 --- a/src/libopensc/card-iasecc.c +++ b/src/libopensc/card-iasecc.c @@ -2548,7 +2548,7 @@ iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { /* 5A 0A 92 50 00 20 10 10 25 00 01 3F */ /* 00 02 01 01 02 50 00 13 */ - for (ii=0; ii < rbuf[1] - offs; ii++) + for (ii=0; (ii < rbuf[1] - offs) && (ii + offs + 2 < sizeof(rbuf)); ii++) *(card->serialnr.value + ii) = ((rbuf[ii + offs + 1] & 0x0F) << 4) + ((rbuf[ii + offs + 2] & 0xF0) >> 4) ; card->serialnr.len = ii; diff --git a/src/libopensc/card-oberthur.c b/src/libopensc/card-oberthur.c index 818a6a6c..112367b5 100644 --- a/src/libopensc/card-oberthur.c +++ b/src/libopensc/card-oberthur.c @@ -476,6 +476,9 @@ auth_select_file(struct sc_card *card, const struct sc_path *in_path, memcpy(&path, in_path, sizeof(struct sc_path)); + if (!auth_current_df) + return SC_ERROR_OBJECT_NOT_FOUND; + sc_log(card->ctx, "in_path; type=%d, path=%s, out %p", in_path->type, sc_print_path(in_path), file_out); sc_log(card->ctx, "current path; type=%d, path=%s", @@ -2113,6 +2116,10 @@ auth_read_binary(struct sc_card *card, unsigned int offset, bn[1].data = NULL; LOG_FUNC_CALLED(card->ctx); + + if (!auth_current_ef) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid auth_current_ef"); + sc_log(card->ctx, "offset %i; size %"SC_FORMAT_LEN_SIZE_T"u; flags 0x%lX", offset, count, flags); diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c index 594ff052..5fa38992 100644 --- a/src/libopensc/card-openpgp.c +++ b/src/libopensc/card-openpgp.c @@ -1061,6 +1061,9 @@ pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) const u8 *data = in; pgp_blob_t *new; + if (!in) + return SC_ERROR_OBJECT_NOT_VALID; + r = sc_asn1_read_tag(&data, blob->len - (in - blob->data), &cla, &tag, &len); if (r < 0 || data == NULL) { @@ -1069,6 +1072,9 @@ pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) return SC_ERROR_OBJECT_NOT_VALID; } + if (data + len > blob->data + blob->len) + return SC_ERROR_OBJECT_NOT_VALID; + /* undo ASN1's split of tag & class */ for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) { cla <<= 8; diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index 13b0cc21..0cd3f5ad 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -573,7 +573,7 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, * the buffer is bigger, so it will not produce "ASN1.tag too long!" */ body = rbuf; - if (sc_asn1_read_tag(&body, 0xffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS + if (sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen) != SC_SUCCESS || body == NULL) { /* only early beta cards had this problem */ sc_log(card->ctx, "***** received buffer tag MISSING "); @@ -3033,12 +3033,13 @@ static int piv_match_card_continued(sc_card_t *card) * 73 66 74 65 20 63 64 31 34 34 * will check for 73 66 74 65 */ - else if (card->reader->atr_info.hist_bytes_len >= 4 && - !(memcmp(card->reader->atr_info.hist_bytes, "sfte", 4))) { + else if (card->reader->atr_info.hist_bytes_len >= 4 + && !(memcmp(card->reader->atr_info.hist_bytes, "sfte", 4))) { type = SC_CARD_TYPE_PIV_II_GI_DE; } - else if (card->reader->atr_info.hist_bytes[0] == 0x80u) { /* compact TLV */ + else if (card->reader->atr_info.hist_bytes_len > 0 + && card->reader->atr_info.hist_bytes[0] == 0x80u) { /* compact TLV */ size_t datalen; const u8 *data = sc_compacttlv_find_tag(card->reader->atr_info.hist_bytes + 1, card->reader->atr_info.hist_bytes_len - 1, diff --git a/src/libopensc/card-rtecp.c b/src/libopensc/card-rtecp.c index a1efe22e..32932ae7 100644 --- a/src/libopensc/card-rtecp.c +++ b/src/libopensc/card-rtecp.c @@ -275,7 +275,7 @@ static int rtecp_select_file(sc_card_t *card, set_acl_from_sec_attr(card, file); else r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - if (r) + if (r && !file_out) sc_file_free(file); else { diff --git a/src/libopensc/card-setcos.c b/src/libopensc/card-setcos.c index f6dc0c61..98464c2e 100644 --- a/src/libopensc/card-setcos.c +++ b/src/libopensc/card-setcos.c @@ -789,6 +789,8 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Check all sub-AC definitions within the total AC */ while (len > 1) { /* minimum length = 2 */ int iACLen = buf[iOffset] & 0x0F; + if ((size_t) iACLen > len) + break; iPinCount = -1; /* default no pin required */ iMethod = SC_AC_NONE; /* default no authentication required */ @@ -806,7 +808,10 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Get KeyNumber if available */ if(iKeyLen) { - int iSC = buf[iOffset+iACLen]; + int iSC; + if (len < 1+iACLen) + break; + iSC = buf[iOffset+iACLen]; switch( (iSC>>5) & 0x03 ){ case 0: @@ -825,11 +830,15 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Get PinNumber if available */ if (iACLen > (1+iParmLen+iKeyLen)) { /* check via total length if pin is present */ + if (len < 1+1+1+iParmLen) + break; iKeyRef = buf[iOffset+1+1+iParmLen]; /* PTL + AM-header + parameter-bytes */ iMethod = SC_AC_CHV; } /* Convert SETCOS command to OpenSC command group */ + if (len < 1+2) + break; switch(buf[iOffset+2]){ case 0x2A: /* crypto operation */ iOperation = SC_AC_OP_CRYPTO; @@ -863,7 +872,10 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) iPinCount = iACLen - 1; if (buf[iOffset] & 0x20) { - int iSC = buf[iOffset + iACLen]; + int iSC; + if (len < 1 + iACLen) + break; + iSC = buf[iOffset + iACLen]; switch( (iSC>>5) & 0x03 ) { case 0: @@ -884,6 +896,8 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Pin present ? */ if ( iPinCount > 0 ) { + if (len < 1 + 2) + break; iKeyRef = buf[iOffset + 2]; /* pin ref */ iMethod = SC_AC_CHV; } diff --git a/src/libopensc/ef-gdo.c b/src/libopensc/ef-gdo.c index 39dea2e5..7888fd49 100644 --- a/src/libopensc/ef-gdo.c +++ b/src/libopensc/ef-gdo.c @@ -72,7 +72,7 @@ sc_parse_ef_gdo_content(const unsigned char *gdo, size_t gdo_len, } p += tag_len; - left -= (p - gdo); + left = gdo_len - (p - gdo); } if (!iccsn_found && iccsn_len) diff --git a/src/libopensc/pkcs15-itacns.c b/src/libopensc/pkcs15-itacns.c index eef8ac1f..f0f39590 100644 --- a/src/libopensc/pkcs15-itacns.c +++ b/src/libopensc/pkcs15-itacns.c @@ -550,6 +550,7 @@ static int itacns_add_data_files(sc_pkcs15_card_t *p15card) sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Could not read EF_DatiPersonali: " "keeping generic card name"); + return SC_SUCCESS; } { diff --git a/src/libopensc/pkcs15-tcos.c b/src/libopensc/pkcs15-tcos.c index 74bb32a6..7732ad08 100644 --- a/src/libopensc/pkcs15-tcos.c +++ b/src/libopensc/pkcs15-tcos.c @@ -132,7 +132,7 @@ static int insert_key( int i, rec_no=0; if(prkey_info.path.len>=2) prkey_info.path.len-=2; sc_append_file_id(&prkey_info.path, 0x5349); - if(sc_select_file(card, &prkey_info.path, NULL)!=SC_SUCCESS){ + if(sc_select_file(card, &prkey_info.path, NULL)!=SC_SUCCESS || !f->prop_attr){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Select(%s) failed\n", sc_print_path(&prkey_info.path)); @@ -157,7 +157,8 @@ static int insert_key( if(buf[i]==0xB8) can_crypt++; } } else { - if(sc_select_file(card, &prkey_info.path, &f)!=SC_SUCCESS){ + if(sc_select_file(card, &prkey_info.path, &f)!=SC_SUCCESS + || !f->prop_attr || f->prop_attr_len < 2){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Select(%s) failed\n", sc_print_path(&prkey_info.path)); @@ -245,7 +246,8 @@ static int insert_pin( return 1; } } else { - if(sc_select_file(card, &pin_info.path, &f)!=SC_SUCCESS){ + if(sc_select_file(card, &pin_info.path, &f)!=SC_SUCCESS + || !f->prop_attr || f->prop_attr_len < 4){ sc_debug(ctx, SC_LOG_DEBUG_NORMAL,"Select(%s) failed\n", path); return 1; } diff --git a/src/tools/opensc-tool.c b/src/tools/opensc-tool.c index 68c72805..2f121acf 100644 --- a/src/tools/opensc-tool.c +++ b/src/tools/opensc-tool.c @@ -457,7 +457,7 @@ static int enum_dir(sc_path_t path, int depth) { sc_file_t *file; int r, file_type; - u8 files[SC_MAX_APDU_BUFFER_SIZE]; + u8 files[SC_MAX_EXT_APDU_BUFFER_SIZE]; r = sc_lock(card); if (r == SC_SUCCESS) @@ -483,15 +483,16 @@ static int enum_dir(sc_path_t path, int depth) } if (r == 0) { printf("Empty directory\n"); - } else - for (i = 0; i < r/2; i++) { - sc_path_t tmppath; + } else { + for (i = 0; i < r/2; i++) { + sc_path_t tmppath; - memset(&tmppath, 0, sizeof(tmppath)); - memcpy(&tmppath, &path, sizeof(path)); - memcpy(tmppath.value + tmppath.len, files + 2*i, 2); - tmppath.len += 2; - enum_dir(tmppath, depth + 1); + memset(&tmppath, 0, sizeof(tmppath)); + memcpy(&tmppath, &path, sizeof(path)); + memcpy(tmppath.value + tmppath.len, files + 2*i, 2); + tmppath.len += 2; + enum_dir(tmppath, depth + 1); + } } } return 0;