2013-09-03 19:01:17 +00:00
|
|
|
From a447d4fc0ece3d1f0ae20c89ddf4b01d7afdb553 Mon Sep 17 00:00:00 2001
|
2013-08-18 10:25:16 +00:00
|
|
|
From: Alon Levy <alevy@redhat.com>
|
|
|
|
Date: Mon, 4 Mar 2013 18:39:09 +0200
|
2013-09-03 19:01:17 +00:00
|
|
|
Subject: [PATCH] ccid-card-passthru: add atr check
|
2013-08-18 10:25:16 +00:00
|
|
|
MIME-Version: 1.0
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
|
|
|
|
Signed-off-by: Alon Levy <alevy@redhat.com>
|
|
|
|
Reviewed-by: Marc-André Lureau <mlureau@redhat.com>
|
|
|
|
(cherry picked from commit 0e61400c1941aabc9f45d5ff961b57337c7caac6)
|
|
|
|
---
|
|
|
|
hw/ccid-card-passthru.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
1 file changed, 59 insertions(+)
|
|
|
|
|
|
|
|
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
|
|
|
|
index 62070db..7fecd10 100644
|
|
|
|
--- a/hw/ccid-card-passthru.c
|
|
|
|
+++ b/hw/ccid-card-passthru.c
|
|
|
|
@@ -138,6 +138,59 @@ static void ccid_card_vscard_handle_init(
|
|
|
|
ccid_card_vscard_send_init(card);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static int check_atr(PassthruState *card, uint8_t *data, int len)
|
|
|
|
+{
|
|
|
|
+ int historical_length, opt_bytes;
|
|
|
|
+ int td_count = 0;
|
|
|
|
+ int td;
|
|
|
|
+
|
|
|
|
+ if (len < 2) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ historical_length = data[1] & 0xf;
|
|
|
|
+ opt_bytes = 0;
|
|
|
|
+ if (data[0] != 0x3b && data[0] != 0x3f) {
|
|
|
|
+ DPRINTF(card, D_WARN, "atr's T0 is 0x%X, not in {0x3b, 0x3f}\n",
|
|
|
|
+ data[0]);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ td_count = 0;
|
|
|
|
+ td = data[1] >> 4;
|
|
|
|
+ while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) {
|
|
|
|
+ td_count++;
|
|
|
|
+ if (td & 0x1) {
|
|
|
|
+ opt_bytes++;
|
|
|
|
+ }
|
|
|
|
+ if (td & 0x2) {
|
|
|
|
+ opt_bytes++;
|
|
|
|
+ }
|
|
|
|
+ if (td & 0x4) {
|
|
|
|
+ opt_bytes++;
|
|
|
|
+ }
|
|
|
|
+ if (td & 0x8) {
|
|
|
|
+ opt_bytes++;
|
|
|
|
+ td = data[opt_bytes + 2] >> 4;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (len < 2 + historical_length + opt_bytes) {
|
|
|
|
+ DPRINTF(card, D_WARN,
|
|
|
|
+ "atr too short: len %d, but historical_len %d, T1 0x%X\n",
|
|
|
|
+ len, historical_length, data[1]);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ if (len > 2 + historical_length + opt_bytes) {
|
|
|
|
+ DPRINTF(card, D_WARN,
|
|
|
|
+ "atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n",
|
|
|
|
+ len, historical_length, opt_bytes, data[1]);
|
|
|
|
+ /* let it through */
|
|
|
|
+ }
|
|
|
|
+ DPRINTF(card, D_VERBOSE,
|
|
|
|
+ "atr passes check: %d total length, %d historical, %d optional\n",
|
|
|
|
+ len, historical_length, opt_bytes);
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void ccid_card_vscard_handle_message(PassthruState *card,
|
|
|
|
VSCMsgHeader *scr_msg_header)
|
|
|
|
{
|
|
|
|
@@ -152,6 +205,12 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
|
|
|
|
VSC_GENERAL_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
+ if (!check_atr(card, data, scr_msg_header->length)) {
|
|
|
|
+ error_report("ATR is inconsistent, ignoring");
|
|
|
|
+ ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
|
|
|
|
+ VSC_GENERAL_ERROR);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
memcpy(card->atr, data, scr_msg_header->length);
|
|
|
|
card->atr_length = scr_msg_header->length;
|
|
|
|
ccid_card_card_inserted(&card->base);
|