From aadd82bb071e574fc57263a103e3bf06ebbd8de7 Mon Sep 17 00:00:00 2001 From: "Ingo Struck (git commits)" Date: Sat, 21 Jan 2023 22:15:10 +0100 Subject: [PATCH] Handle reader limits for SC Card unwrap operations Fixes #2514 --- src/libopensc/card-sc-hsm.c | 181 ++++++++++++++----------- src/libopensc/reader-pcsc.c | 91 ++++++++----- src/tests/fuzzing/fuzz_pkcs15_decode.c | 3 +- src/tests/fuzzing/fuzz_pkcs15_encode.c | 2 +- 4 files changed, 159 insertions(+), 118 deletions(-) diff --git a/src/libopensc/card-sc-hsm.c b/src/libopensc/card-sc-hsm.c index 60d5895127..1b707f08df 100644 --- a/src/libopensc/card-sc-hsm.c +++ b/src/libopensc/card-sc-hsm.c @@ -145,9 +145,7 @@ static int sc_hsm_select_file_ex(sc_card_t *card, if (file_out == NULL) { // Versions before 0.16 of the SmartCard-HSM do not support P2='0C' rv = sc_hsm_select_file_ex(card, in_path, forceselect, &file); - if (file != NULL) { - sc_file_free(file); - } + sc_file_free(file); return rv; } @@ -181,9 +179,7 @@ static int sc_hsm_select_file_ex(sc_card_t *card, LOG_TEST_RET(card->ctx, rv, "Could not select SmartCard-HSM application"); if (priv) { - if (priv->dffcp != NULL) { - sc_file_free(priv->dffcp); - } + sc_file_free(priv->dffcp); // Cache the FCP returned when selecting the applet sc_file_dup(&priv->dffcp, *file_out); } @@ -730,12 +726,12 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; #ifdef ENABLE_SM if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) { - sc_log(card->ctx, + sc_log(card->ctx, "Session PIN generation only supported in SM"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } #else - sc_log(card->ctx, + sc_log(card->ctx, "Session PIN generation only supported in SM"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); #endif @@ -746,7 +742,7 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, apdu.le = 0; if (sc_transmit_apdu(card, &apdu) != SC_SUCCESS || sc_check_sw(card, apdu.sw1, apdu.sw2) != SC_SUCCESS) { - sc_log(card->ctx, + sc_log(card->ctx, "Generating session PIN failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -756,12 +752,12 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, apdu.resplen); data->pin2.len = apdu.resplen; } else { - sc_log(card->ctx, + sc_log(card->ctx, "Buffer too small for session PIN"); } } } else { - sc_log(card->ctx, + sc_log(card->ctx, "Session PIN not supported for this PIN (0x%02X)", data->pin_reference); } @@ -848,47 +844,61 @@ static int sc_hsm_write_ef(sc_card_t *card, LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } - p = cmdbuff; - *p++ = 0x54; - *p++ = 0x02; - *p++ = (idx >> 8) & 0xFF; - *p++ = idx & 0xFF; - *p++ = 0x53; - if (count < 128) { - *p++ = (u8) count; - len = 6; - } else if (count < 256) { - *p++ = 0x81; - *p++ = (u8) count; - len = 7; - } else { - *p++ = 0x82; - *p++ = (count >> 8) & 0xFF; - *p++ = count & 0xFF; - len = 8; - } + size_t bytes_left = count; + // 8 bytes are required for T54(4) and T53(4) + size_t blk_size = card->max_send_size - 8; + size_t to_send = 0; + size_t offset = (size_t) idx; + do { + len = 0; + to_send = bytes_left >= blk_size ? blk_size : bytes_left; + p = cmdbuff; + // ASN1 0x54 offset + *p++ = 0x54; + *p++ = 0x02; + *p++ = (offset >> 8) & 0xFF; + *p++ = offset & 0xFF; + // ASN1 0x53 to_send + *p++ = 0x53; + if (to_send < 128) { + *p++ = (u8)to_send; + len = 6; + } else if (to_send < 256) { + *p++ = 0x81; + *p++ = (u8)to_send; + len = 7; + } else { + *p++ = 0x82; + *p++ = (to_send >> 8) & 0xFF; + *p++ = to_send & 0xFF; + len = 8; + } - if (buf != NULL) - memcpy(p, buf, count); - len += count; + if (buf != NULL) + memcpy(p, buf+offset, to_send); + len += to_send; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, fid >> 8, fid & 0xFF); - apdu.data = cmdbuff; - apdu.datalen = len; - apdu.lc = len; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, fid >> 8, fid & 0xFF); + apdu.data = cmdbuff; + apdu.datalen = len; + apdu.lc = len; - r = sc_transmit_apdu(card, &apdu); - free(cmdbuff); - LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(ctx, r, "Check SW error"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, r, "Check SW error"); + bytes_left -= to_send; + offset += to_send; + } while (0 < bytes_left); + +err: + free(cmdbuff); LOG_FUNC_RETURN(ctx, count); } - static int sc_hsm_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) @@ -1227,7 +1237,7 @@ static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *pa return SC_ERROR_INVALID_ARGUMENTS; } *p++ = 0x81; // User PIN - *p++ = (u8) params->user_pin_len; + *p++ = (u8)params->user_pin_len; memcpy(p, params->user_pin, params->user_pin_len); p += params->user_pin_len; @@ -1400,12 +1410,11 @@ static int sc_hsm_unwrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *p LOG_FUNC_CALLED(card->ctx); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, 0x74, params->key_id, 0x93); - apdu.cla = 0x80; - apdu.lc = params->wrapped_key_length; - apdu.data = params->wrapped_key; - apdu.datalen = params->wrapped_key_length; + r = sc_hsm_write_ef(card, 0x2F10, 0, params->wrapped_key, params->wrapped_key_length); + LOG_TEST_RET(card->ctx, r, "Create EF failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x74, params->key_id, 0x93); + apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); @@ -1765,17 +1774,10 @@ static int sc_hsm_init(struct sc_card *card) int flags,ext_flags; sc_file_t *file = NULL; sc_path_t path; - sc_hsm_private_data_t *priv = card->drv_data; + sc_hsm_private_data_t *priv = NULL; LOG_FUNC_CALLED(card->ctx); - if (!priv) { - priv = calloc(1, sizeof(sc_hsm_private_data_t)); - if (!priv) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - card->drv_data = priv; - } - flags = SC_ALGORITHM_RSA_RAW|SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_rsa_alg(card, 1024, flags, 0); @@ -1807,6 +1809,46 @@ static int sc_hsm_init(struct sc_card *card) card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT|SC_CARD_CAP_ISO7816_PIN_INFO; + // APDU Buffer limits + // JCOP 2.4.1r3 1462 + // JCOP 2.4.2r3 1454 + // JCOP 3 1232 + // MicroSD with JCOP 3 478 / 506 - handled in reader-pcsc.c + // Reiner SCT 1014 - handled in reader-pcsc.c + + // Use JCOP 3 card limits for sending + card->max_send_size = 1232; + // Assume that card supports sending with extended length APDU and without limit + card->max_recv_size = 0; + + if (card->type == SC_CARD_TYPE_SC_HSM_SOC + || card->type == SC_CARD_TYPE_SC_HSM_GOID) { + card->max_recv_size = 0x0630; // SoC Proxy forces this limit + } else { + // Adjust to the limits set by the reader + if (card->reader->max_send_size < card->max_send_size) { + if (18 >= card->reader->max_send_size) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCONSISTENT_CONFIGURATION); + + // 17 byte header and TLV because of odd ins in UPDATE BINARY + card->max_send_size = card->reader->max_send_size - 17; + } + + if (0 < card->reader->max_recv_size) { + if (3 >= card->reader->max_recv_size) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCONSISTENT_CONFIGURATION); + card->max_recv_size = card->reader->max_recv_size - 2; + } + } + + priv = card->drv_data; + if (!priv) { + priv = calloc(1, sizeof(sc_hsm_private_data_t)); + if (!priv) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + card->drv_data = priv; + } + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); if (sc_hsm_select_file_ex(card, &path, 0, &file) == SC_SUCCESS && file && file->prop_attr && file->prop_attr_len >= 2) { @@ -1839,25 +1881,6 @@ static int sc_hsm_init(struct sc_card *card) } sc_file_free(file); - // APDU Buffer limits - // JCOP 2.4.1r3 1462 - // JCOP 2.4.2r3 1454 - // JCOP 3 1232 - // MicroSD with JCOP 3 478 / 506 - // Reiner SCT 1014 - - card->max_send_size = 1232 - 17; // 1232 buffer size - 17 byte header and TLV because of odd ins in UPDATE BINARY - - if (!strncmp("Secure Flash Card", card->reader->name, 17)) { - card->max_send_size = 478 - 17; - card->max_recv_size = 506 - 2; - } else if (card->type == SC_CARD_TYPE_SC_HSM_SOC - || card->type == SC_CARD_TYPE_SC_HSM_GOID) { - card->max_recv_size = 0x0630; // SoC Proxy forces this limit - } else { - card->max_recv_size = 0; // Card supports sending with extended length APDU and without limit - } - priv->EF_C_DevAut = NULL; priv->EF_C_DevAut_len = 0; @@ -1883,13 +1906,11 @@ static int sc_hsm_finish(sc_card_t * card) #ifdef ENABLE_SM sc_sm_stop(card); #endif - if (priv->serialno) { + if (priv) { free(priv->serialno); - } - if (priv->dffcp) { sc_file_free(priv->dffcp); + free(priv->EF_C_DevAut); } - free(priv->EF_C_DevAut); free(priv); return SC_SUCCESS; diff --git a/src/libopensc/reader-pcsc.c b/src/libopensc/reader-pcsc.c index 40bfd293d3..04d5ac8fdd 100644 --- a/src/libopensc/reader-pcsc.c +++ b/src/libopensc/reader-pcsc.c @@ -311,7 +311,7 @@ static int pcsc_transmit(sc_reader_t *reader, sc_apdu_t *apdu) * The buffer for the returned data needs to be at least 2 bytes * larger than the expected data length to store SW1 and SW2. */ rsize = rbuflen = apdu->resplen <= 256 ? 258 : apdu->resplen + 2; - rbuf = malloc(rbuflen); + rbuf = malloc(rbuflen); if (rbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; @@ -386,7 +386,7 @@ static int refresh_attributes(sc_reader_t *reader) } LOG_FUNC_RETURN(reader->ctx, SC_SUCCESS); } - + /* the system could not detect the reader. It means, the prevoiusly attached reader is disconnected. */ if (rv == (LONG)SCARD_E_UNKNOWN_READER #ifdef SCARD_E_NO_READERS_AVAILABLE @@ -424,7 +424,7 @@ static int refresh_attributes(sc_reader_t *reader) if (priv->reader_state.cbAtr > SC_MAX_ATR_SIZE) return SC_ERROR_INTERNAL; - /* Some cards have a different cold (after a powerup) and warm (after a reset) ATR */ + /* Some cards have a different cold (after a powerup) and warm (after a reset) ATR */ if (memcmp(priv->reader_state.rgbAtr, reader->atr.value, priv->reader_state.cbAtr) != 0) { reader->atr.len = priv->reader_state.cbAtr; memcpy(reader->atr.value, priv->reader_state.rgbAtr, reader->atr.len); @@ -556,7 +556,7 @@ static int pcsc_reconnect(sc_reader_t * reader, DWORD action) priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol, action, &active_proto); - + PCSC_TRACE(reader, "SCardReconnect returned", rv); if (rv != SCARD_S_SUCCESS) { PCSC_TRACE(reader, "SCardReconnect failed", rv); @@ -593,7 +593,7 @@ static void initialize_uid(sc_reader_t *reader) sc_log_hex(reader->ctx, "UID", reader->uid.value, reader->uid.len); } else { - sc_log(reader->ctx, "unable to get UID"); + sc_log(reader->ctx, "unable to get UID"); } } } @@ -1177,7 +1177,7 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) priv->modify_ioctl_finish = ntohl(pcsc_tlv->value); } else if (pcsc_tlv->tag == FEATURE_IFD_PIN_PROPERTIES) { priv->pin_properties_ioctl = ntohl(pcsc_tlv->value); - } else if (pcsc_tlv->tag == FEATURE_GET_TLV_PROPERTIES) { + } else if (pcsc_tlv->tag == FEATURE_GET_TLV_PROPERTIES) { priv->get_tlv_properties = ntohl(pcsc_tlv->value); } else if (pcsc_tlv->tag == FEATURE_EXECUTE_PACE) { priv->pace_ioctl = ntohl(pcsc_tlv->value); @@ -1240,11 +1240,11 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) sc_log(ctx, "Reader has a display: %04X", caps->wLcdLayout); reader->capabilities |= SC_READER_CAP_DISPLAY; } - else { + else { sc_log(ctx, "Reader does not have a display."); } } - else { + else { sc_log(ctx, "Returned PIN properties structure has bad length (%lu/%"SC_FORMAT_LEN_SIZE_T"u)", (unsigned long)rcount, @@ -1266,34 +1266,55 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } } + size_t max_send_size = 0; + size_t max_recv_size = 0; if (priv->get_tlv_properties) { /* Try to set reader max_send_size and max_recv_size based on * detected max_data */ - int max_data = part10_detect_max_data(reader, card_handle); - - if (max_data > 0) { - sc_log(ctx, "Reader supports transceiving %d bytes of data", - max_data); - if (!priv->gpriv->force_max_send_size) - reader->max_send_size = max_data; - else - sc_log(ctx, "Sending is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" - " in configuration file", reader->max_send_size); - if (!priv->gpriv->force_max_recv_size) - reader->max_recv_size = max_data; - else - sc_log(ctx, "Receiving is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" - " in configuration file", reader->max_recv_size); - } else { - sc_log(ctx, "Assuming that the reader supports transceiving " - "short length APDUs only"); - } + max_send_size = max_recv_size = part10_detect_max_data(reader, card_handle); /* debug the product and vendor ID of the reader */ part10_get_vendor_product(reader, card_handle, NULL, NULL); } + else { + /* Try to set default limits based on device name */ + if (!strncmp("REINER SCT cyberJack", reader->name, 20)) { + max_send_size = 1014; + max_recv_size = 1014; + } + else if (!strncmp("Secure Flash Card", reader->name, 17)) { + max_send_size = 478; + max_recv_size = 506; + } + } - if(gpriv->SCardGetAttrib != NULL) { + if (max_send_size > 0) { + sc_log(ctx, "Reader supports sending %"SC_FORMAT_LEN_SIZE_T"u bytes of data", + max_send_size); + if (!priv->gpriv->force_max_send_size) + reader->max_send_size = max_send_size; + else + sc_log(ctx, "Sending is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" + " in configuration file", reader->max_send_size); + } else { + sc_log(ctx, "Assuming that the reader supports sending " + "short length APDUs only"); + } + + if (max_recv_size > 0) { + sc_log(ctx, "Reader supports receiving %"SC_FORMAT_LEN_SIZE_T"u bytes of data", + max_recv_size); + if (!priv->gpriv->force_max_recv_size) + reader->max_recv_size = max_recv_size; + else + sc_log(ctx, "Receiving is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" + " in configuration file", reader->max_recv_size); + } else { + sc_log(ctx, "Assuming that the reader supports receiving " + "short length APDUs only"); + } + + if (gpriv->SCardGetAttrib != NULL) { rcount = sizeof(buf); if (gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_NAME, buf, &rcount) == SCARD_S_SUCCESS @@ -1304,7 +1325,7 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } rcount = sizeof i; - if(gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_IFD_VERSION, + if (gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_IFD_VERSION, (u8 *) &i, &rcount) == SCARD_S_SUCCESS && rcount == sizeof i) { reader->version_major = (i >> 24) & 0xFF; @@ -1314,7 +1335,7 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } int pcsc_add_reader(sc_context_t *ctx, - char *reader_name, size_t reader_name_len, + char *reader_name, size_t reader_name_len, sc_reader_t **out_reader) { int ret = SC_ERROR_INTERNAL; @@ -1574,7 +1595,7 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re LOG_FUNC_CALLED(ctx); - if (!event_reader && !event && reader_states) { + if (!event_reader && !event && reader_states) { sc_log(ctx, "free allocated reader states"); free(*reader_states); *reader_states = NULL; @@ -1684,7 +1705,7 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re state = rsp->dwEventState; rsp->dwCurrentState = rsp->dwEventState; if (state & SCARD_STATE_CHANGED) { - /* check for hotplug events */ + /* check for hotplug events */ if (!strcmp(rsp->szReader, "\\\\?PnP?\\Notification")) { sc_log(ctx, "detected hotplug event"); /* Windows sends hotplug event on both, attaching and @@ -1859,7 +1880,7 @@ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, siz u8 tmp; unsigned int tmp16; unsigned int off; - PIN_VERIFY_STRUCTURE *pin_verify = (PIN_VERIFY_STRUCTURE *)buf; + PIN_VERIFY_STRUCTURE *pin_verify = (PIN_VERIFY_STRUCTURE *)buf; /* PIN verification control message */ pin_verify->bTimerOut = SC_CCID_PIN_TIMEOUT; @@ -1958,7 +1979,7 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz sc_apdu_t *apdu = data->apdu; u8 tmp; unsigned int tmp16; - PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf; + PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf; struct sc_pin_cmd_pin *pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? &data->pin2 : &data->pin1; @@ -2569,7 +2590,7 @@ int pcsc_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_c gpriv->attached_reader = NULL; gpriv->pcsc_ctx = *(SCARDCONTEXT *)pcsc_context_handle; - card_handle = *(SCARDHANDLE *)pcsc_card_handle; + card_handle = *(SCARDHANDLE *)pcsc_card_handle; if(SCARD_S_SUCCESS == gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_DEVICE_SYSTEM_NAME_A, (LPBYTE) diff --git a/src/tests/fuzzing/fuzz_pkcs15_decode.c b/src/tests/fuzzing/fuzz_pkcs15_decode.c index a83c719cb9..e5758ba4d5 100644 --- a/src/tests/fuzzing/fuzz_pkcs15_decode.c +++ b/src/tests/fuzzing/fuzz_pkcs15_decode.c @@ -108,9 +108,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) sc_pkcs15_parse_unusedspace(buf, buf_len, p15card); - sc_pkcs15_card_free(p15card); - err: + sc_pkcs15_card_free(p15card); sc_disconnect_card(card); sc_release_context(ctx); return 0; diff --git a/src/tests/fuzzing/fuzz_pkcs15_encode.c b/src/tests/fuzzing/fuzz_pkcs15_encode.c index eb3436dae2..a10ecf5645 100644 --- a/src/tests/fuzzing/fuzz_pkcs15_encode.c +++ b/src/tests/fuzzing/fuzz_pkcs15_encode.c @@ -80,8 +80,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) sc_pkcs15_encode_unusedspace(ctx, p15card, &unused_space, &unused_space_len); free(unused_space); - sc_pkcs15_card_free(p15card); err: + sc_pkcs15_card_free(p15card); sc_disconnect_card(card); sc_release_context(ctx);