diff options
author | Martin Kaiser <wireshark@kaiser.cx> | 2013-01-15 16:41:03 +0000 |
---|---|---|
committer | Martin Kaiser <wireshark@kaiser.cx> | 2013-01-15 16:41:03 +0000 |
commit | e00af44a363a4a672e34ea22d753faf0e987c147 (patch) | |
tree | 1bbd3ad4ed6c0023547d3e56093990e2000e5b61 /epan/dissectors/packet-iso7816.c | |
parent | 5a74d61822ff9691e2da29b201a36cc00f2f7558 (diff) |
extend the iso7816 dissector
link request apdus to response apdus
dissect parameters p1 and p2 for some messages
svn path=/trunk/; revision=47096
Diffstat (limited to 'epan/dissectors/packet-iso7816.c')
-rw-r--r-- | epan/dissectors/packet-iso7816.c | 277 |
1 files changed, 258 insertions, 19 deletions
diff --git a/epan/dissectors/packet-iso7816.c b/epan/dissectors/packet-iso7816.c index 3e77880441..9b37a6e710 100644 --- a/epan/dissectors/packet-iso7816.c +++ b/epan/dissectors/packet-iso7816.c @@ -1,6 +1,6 @@ /* packet-iso7816.c * Routines for packet dissection of generic ISO 7816 smart card messages - * Copyright 2012 by Martin Kaiser <martin@kaiser.cx> + * Copyright 2012-2013 by Martin Kaiser <martin@kaiser.cx> * * $Id$ * @@ -44,8 +44,13 @@ static int proto_iso7816_atr = -1; static dissector_handle_t iso7816_atr_handle; +static emem_tree_t *transactions = NULL; + static int ett_iso7816 = -1; static int ett_iso7816_class = -1; +static int ett_iso7816_param = -1; +static int ett_iso7816_p1 = -1; +static int ett_iso7816_p2 = -1; static int ett_iso7816_atr = -1; static int ett_iso7816_atr_td = -1; @@ -63,6 +68,9 @@ static int hf_iso7816_atr_k = -1; static int hf_iso7816_atr_t = -1; static int hf_iso7816_atr_hist_bytes = -1; static int hf_iso7816_atr_tck = -1; + +static int hf_iso7816_resp_in = -1; +static int hf_iso7816_resp_to = -1; static int hf_iso7816_cla = -1; static int hf_iso7816_cla_sm = -1; static int hf_iso7816_cla_channel = -1; @@ -74,10 +82,23 @@ static int hf_iso7816_le = -1; static int hf_iso7816_body = -1; static int hf_iso7816_sw1 = -1; static int hf_iso7816_sw2 = -1; +static int hf_iso7816_sel_file_ctrl = -1; +static int hf_iso7816_sel_file_fci_req = -1; +static int hf_iso7816_sel_file_occ = -1; #define ADDR_INTF "Interface" #define ADDR_CARD "Card" +typedef struct _iso7816_transaction_t { + guint32 cmd_frame; + guint32 resp_frame; + guint8 cmd_ins; /* instruction byte in the command apdu */ + /* no need to add the channel number, + the response contains no channel number to compare this to + and the spec explicitly prohibits interleaving of command-response + pairs, regardless of logical channels */ +} iso7816_transaction_t; + static const value_string iso7816_atr_init_char[] = { { 0x3B, "Direct convention (A==0, Z==1, MSB==m9)" }, { 0x3F, "Inverse convention (A==1, Z==0, MSB==m2)" }, @@ -108,6 +129,8 @@ static const value_string iso7816_cla_sm[] = { #define INS_PUT_DATA 0xDA #define INS_UPDATE_REC 0xDC #define INS_APPEND_REC 0xE2 +/* for our transaction tracking, not defined in the specification */ +#define INS_INVALID 0x00 static const value_string iso7816_ins[] = { /* instructions defined in ISO 7816-4 */ @@ -131,6 +154,40 @@ static const value_string iso7816_ins[] = { }; static value_string_ext iso7816_ins_ext = VALUE_STRING_EXT_INIT(iso7816_ins); +#define P1P2 (p1<<8|p2) + +static const value_string iso7816_sel_file_ctrl[] = { + { 0x00, "Select MF, DF or EF" }, + { 0x01, "Select child DF" }, + { 0x02, "Select EF under current DF" }, + { 0x03, "Select parent DF of the current DF" }, + { 0x04, "Direct selection by DF name" }, + { 0x08, "Selection by path from MF" }, + { 0x09, "Selection by path from current DF" }, + { 0, NULL } +}; +static value_string_ext ext_iso7816_sel_file_ctrl = + VALUE_STRING_EXT_INIT(iso7816_sel_file_ctrl); + +static const value_string iso7816_sel_file_fci_req[] = { + { 0x00, "Return FCI, optional template" }, + { 0x01, "Return FCP template" }, + { 0x02, "Return FMD template" }, + { 0, NULL } +}; +static value_string_ext ext_iso7816_sel_file_fci_req = + VALUE_STRING_EXT_INIT(iso7816_sel_file_fci_req); + +static const value_string iso7816_sel_file_occ[] = { + { 0x00, "First or only occurrence" }, + { 0x01, "Last occurrence" }, + { 0x02, "Next occurrence" }, + { 0x03, "Previous occurrence" }, + { 0, NULL } +}; +static value_string_ext ext_iso7816_sel_file_occ = + VALUE_STRING_EXT_INIT(iso7816_sel_file_occ); + static const range_string iso7816_sw1[] = { { 0x61, 0x61, "Normal processing" }, { 0x62, 0x63, "Warning processing" }, @@ -141,6 +198,14 @@ static const range_string iso7816_sw1[] = { }; +static void +iso7816_init(void) +{ + transactions = se_tree_create_non_persistent( + EMEM_TREE_TYPE_RED_BLACK, "iso7816_transactions"); +} + + static int dissect_iso7816_atr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { @@ -303,7 +368,6 @@ dissect_iso7816_class(tvbuff_t *tvb, gint offset, else { enc_item = proto_tree_add_text(class_tree, tvb, offset, 1, "structure and coding according to ISO/IEC 7816"); - if (class>=0xA0 && class<=0xAF) { proto_item_append_text(enc_item, " unless specified otherwise by the application context"); @@ -326,7 +390,95 @@ dissect_iso7816_class(tvbuff_t *tvb, gint offset, return ret_fct; } -static int +/* dissect the parameters p1 and p2 + return number of dissected bytes or -1 for error */ +static gint +dissect_iso7816_params(guint8 ins, tvbuff_t *tvb, gint offset, + packet_info *pinfo _U_, proto_tree *tree) +{ + gint offset_start, p1_offset, p2_offset; + proto_item *ti; + proto_tree *params_tree = NULL; + guint8 p1, p2; + proto_item *p1_it = NULL, *p2_it = NULL; + proto_tree *p1_tree = NULL, *p2_tree = NULL; + proto_item *p1_p2_it = NULL; + + offset_start = offset; + + ti = proto_tree_add_text(tree, tvb, offset_start, 2, "Parameters"); + params_tree = proto_item_add_subtree(ti, ett_iso7816_param); + + p1 = tvb_get_guint8(tvb,offset); + p1_it = proto_tree_add_item(params_tree, hf_iso7816_p1, tvb, + offset, 1, ENC_BIG_ENDIAN); + p1_offset = offset; + offset++; + p2 = tvb_get_guint8(tvb,offset); + p2_it = proto_tree_add_item(params_tree, hf_iso7816_p2, + tvb, offset, 1, ENC_BIG_ENDIAN); + p2_offset = offset; + offset++; + + switch (ins) { + case INS_EXT_AUTH: + if (p1>0) { + proto_item_append_text(p1_it, + " (reference of the algorithm on the card)"); + } + proto_item_append_text(p2_it, " (reference of the secret)"); + break; + case INS_SELECT_FILE: + proto_item_append_text(p1_it, " (selection control)"); + p1_tree = proto_item_add_subtree(p1_it, ett_iso7816_p1); + proto_tree_add_item(p1_tree, hf_iso7816_sel_file_ctrl, + tvb, p1_offset, 1, ENC_BIG_ENDIAN); + proto_item_append_text(p2_it, " (selection options)"); + p2_tree = proto_item_add_subtree(p2_it, ett_iso7816_p2); + proto_tree_add_item(p2_tree, hf_iso7816_sel_file_fci_req, + tvb, p2_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(p2_tree, hf_iso7816_sel_file_occ, + tvb, p2_offset, 1, ENC_BIG_ENDIAN); + break; + case INS_READ_BIN: + if (p1&0x80) { + /* XXX - b5-b1 of P1 == short ef identifier for the selected file */ + /* XXX - P2 == offset for the read */ + } + else { + p1_p2_it = proto_tree_add_text( + params_tree, tvb, offset_start, offset-offset_start, + "Offset of the first byte to read: %d", P1P2); + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, + "offset %d", P1P2); + } + break; + case INS_GET_RESP: + p1_p2_it = proto_tree_add_text( + params_tree, tvb, offset_start, offset-offset_start, + "Both should be 0x00, other values are RFU"); + break; + case INS_GET_DATA: + if (P1P2<=0x003F || (0x0300<=P1P2 && P1P2<=0x3FFF)) { + p1_p2_it = proto_tree_add_text(params_tree, + tvb, offset_start, offset-offset_start, "RFU"); + } + else if (0x0100<=P1P2 && P1P2<=0x01FF) { + p1_p2_it = proto_tree_add_text( + params_tree, tvb, offset_start, offset-offset_start, + "Application data (proprietary coding)"); + } + break; + default: + break; + } + + PROTO_ITEM_SET_GENERATED(p1_p2_it); + + return 2; +} + +static gint dissect_iso7816_le( tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, proto_tree *tree) { @@ -343,16 +495,42 @@ dissect_iso7816_le( } -static int +static gint dissect_iso7816_cmd_apdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { - gint ret; - gint offset = 0; - guint8 ins; - gint body_len; - guint8 lc; - - + iso7816_transaction_t *iso7816_trans = NULL; + proto_item *trans_ti = NULL; + gint ret; + gint offset = 0; + guint8 ins; + gint body_len; + guint8 lc; + + + if (PINFO_FD_VISITED(pinfo)) { + iso7816_trans = (iso7816_transaction_t *)se_tree_lookup32( + transactions, PINFO_FD_NUM(pinfo)); + if (iso7816_trans && iso7816_trans->cmd_frame==PINFO_FD_NUM(pinfo) && + iso7816_trans->resp_frame!=0) { + trans_ti = proto_tree_add_uint_format(tree, hf_iso7816_resp_in, + NULL, 0, 0, iso7816_trans->resp_frame, + "Response in frame %d", iso7816_trans->resp_frame); + PROTO_ITEM_SET_GENERATED(trans_ti); + } + } + else { + if (transactions) { + iso7816_trans = (iso7816_transaction_t *)se_alloc( + sizeof(iso7816_transaction_t)); + iso7816_trans->cmd_frame = PINFO_FD_NUM(pinfo); + iso7816_trans->resp_frame = 0; + iso7816_trans->cmd_ins = INS_INVALID; + + se_tree_insert32(transactions, + iso7816_trans->cmd_frame, (void *)iso7816_trans); + } + } + ret = dissect_iso7816_class(tvb, offset, pinfo, tree); if (ret==-1) { /* the class byte says that the remaining APDU is not @@ -369,14 +547,16 @@ dissect_iso7816_cmd_apdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_ext_const(ins, &iso7816_ins_ext, "Unknown instruction")); offset++; + /* if we just created a new transaction, we can now fill in the cmd id */ + if (iso7816_trans && iso7816_trans->cmd_ins==INS_INVALID) + iso7816_trans->cmd_ins = ins; - proto_tree_add_item(tree, hf_iso7816_p1, tvb, offset, 1, ENC_BIG_ENDIAN); - offset++; - proto_tree_add_item(tree, hf_iso7816_p2, tvb, offset, 1, ENC_BIG_ENDIAN); - offset++; + ret = dissect_iso7816_params(ins, tvb, offset, pinfo, tree); + if (ret>0) + offset += ret; /* for now, we support only short length fields - if we have ATR parsing, we can support extended length fields too */ + based on infos from the ATR, we could support extended length fields too */ body_len = tvb_reported_length_remaining(tvb, offset); /* nothing to do for body_len==0 */ @@ -400,14 +580,44 @@ dissect_iso7816_cmd_apdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) return offset; } -static int +static gint dissect_iso7816_resp_apdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { - gint offset = 0; - gint body_len; + iso7816_transaction_t *iso7816_trans; + proto_item *trans_ti = NULL; + const gchar *cmd_ins_str; + gint offset = 0; + gint body_len; col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Response APDU"); + if (transactions) { + /* receive the largest key that is less than or equal to our frame + number */ + iso7816_trans = (iso7816_transaction_t *)se_tree_lookup32_le( + transactions, PINFO_FD_NUM(pinfo)); + if (iso7816_trans) { + if (iso7816_trans->resp_frame==0) { + /* there's a pending request, this packet is the response */ + iso7816_trans->resp_frame = PINFO_FD_NUM(pinfo); + } + + if (iso7816_trans->resp_frame== PINFO_FD_NUM(pinfo)) { + /* we found the request that corresponds to our response */ + cmd_ins_str = val_to_str_const(iso7816_trans->cmd_ins, + iso7816_ins, "Unknown instruction"); + trans_ti = proto_tree_add_uint_format(tree, hf_iso7816_resp_to, + NULL, 0, 0, iso7816_trans->cmd_frame, + "Response to frame %d (%s)", + iso7816_trans->cmd_frame, cmd_ins_str); + PROTO_ITEM_SET_GENERATED(trans_ti); + + col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", + "(to %s)", cmd_ins_str); + } + } + } + /* - 2 bytes SW1, SW2 */ body_len = tvb_reported_length_remaining(tvb, offset) - 2; @@ -538,6 +748,16 @@ proto_register_iso7816(void) { "Check character TCK", "iso7816.atr.tck", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } }, + { &hf_iso7816_resp_in, + { "Response In", "iso7816.resp_in", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "The response to this command is in this frame", HFILL } + }, + { &hf_iso7816_resp_to, + { "Response To", "iso7816.resp_to", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "This is the response to the command in this frame", HFILL } + }, { &hf_iso7816_cla, { "Class", "iso7816.apdu.cla", FT_UINT8, BASE_HEX, NULL, 0, NULL , HFILL } @@ -581,11 +801,29 @@ proto_register_iso7816(void) { &hf_iso7816_sw2, { "Status Word SW2", "iso7816.apdu.sw2", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } + }, + { &hf_iso7816_sel_file_ctrl, + { "Selection control", "iso7816.apdu.select_file.ctrl", + FT_UINT8, BASE_HEX | BASE_EXT_STRING, + &ext_iso7816_sel_file_ctrl, 0, NULL, HFILL } + }, + { &hf_iso7816_sel_file_fci_req, + { "File control information request", "iso7816.apdu.select_file.fci_req", + FT_UINT8, BASE_HEX | BASE_EXT_STRING, + &ext_iso7816_sel_file_fci_req, 0x0C, NULL, HFILL } + }, + { &hf_iso7816_sel_file_occ, + { "Occurrence", "iso7816.apdu.select_file.occurrence", + FT_UINT8, BASE_HEX | BASE_EXT_STRING, + &ext_iso7816_sel_file_occ, 0x03, NULL, HFILL } } }; static gint *ett[] = { &ett_iso7816, &ett_iso7816_class, + &ett_iso7816_param, + &ett_iso7816_p1, + &ett_iso7816_p2, &ett_iso7816_atr, &ett_iso7816_atr_td }; @@ -596,6 +834,7 @@ proto_register_iso7816(void) proto_register_subtree_array(ett, array_length(ett)); new_register_dissector("iso7816", dissect_iso7816, proto_iso7816); + register_init_routine(iso7816_init); proto_iso7816_atr = proto_register_protocol( "ISO/IEC 7816-3", "ISO 7816-3", "iso7816.atr"); |