diff options
Diffstat (limited to 'utils/osmo-sim-test.c')
-rw-r--r-- | utils/osmo-sim-test.c | 416 |
1 files changed, 329 insertions, 87 deletions
diff --git a/utils/osmo-sim-test.c b/utils/osmo-sim-test.c index cd983975..cf423c49 100644 --- a/utils/osmo-sim-test.c +++ b/utils/osmo-sim-test.c @@ -1,5 +1,5 @@ /* libosmosim test application - currently simply dumps a USIM */ -/* (C) 2012 by Harald Welte <laforge@gnumonks.org> +/* (C) 2012-2020 by Harald Welte <laforge@gnumonks.org> * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,12 @@ #include <getopt.h> #include <arpa/inet.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <limits.h> + #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/sim/sim.h> @@ -33,6 +39,9 @@ /* FIXME: this needs to be moved to card_fs_uicc.c */ +static uint8_t g_class = 0x00; /* UICC/USIM */ +static const char *g_output_dir; + /* 11.1.1 */ static struct msgb *_select_file(struct osim_chan_hdl *st, uint8_t p1, uint8_t p2, const uint8_t *data, uint8_t data_len) @@ -40,7 +49,7 @@ static struct msgb *_select_file(struct osim_chan_hdl *st, uint8_t p1, uint8_t p struct msgb *msg; uint8_t *dst; - msg = osim_new_apdumsg(0x00, 0xA4, p1, p2, data_len, 256); + msg = osim_new_apdumsg(g_class, 0xA4, p1, p2, data_len, 256); dst = msgb_put(msg, data_len); memcpy(dst, data, data_len); @@ -59,10 +68,16 @@ static struct msgb *select_adf(struct osim_chan_hdl *st, const uint8_t *adf, uin static struct msgb *select_file(struct osim_chan_hdl *st, uint16_t fid) { uint16_t cfid = htons(fid); + uint8_t p2 = 0x04; - return _select_file(st, 0x00, 0x04, (uint8_t *)&cfid, 2); + /* Classic SIM cards don't support 0x04 (Return FCP) */ + if (g_class == 0xA0) + p2 = 0x00; + + return _select_file(st, 0x00, p2, (uint8_t *)&cfid, 2); } +#if 0 /* 11.1.9 */ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin) { @@ -72,7 +87,7 @@ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin) if (strlen(pin) > 8) return -EINVAL; - msg = osim_new_apdumsg(0x00, 0x20, 0x00, pin_nr, 8, 0); + msg = osim_new_apdumsg(g_class, 0x20, 0x00, pin_nr, 8, 0); pindst = (char *) msgb_put(msg, 8); memset(pindst, 0xFF, 8); /* Do not copy the terminating \0 */ @@ -80,13 +95,14 @@ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin) return osim_transceive_apdu(st, msg); } +#endif /* 11.1.5 */ static struct msgb *read_record_nr(struct osim_chan_hdl *st, uint8_t rec_nr, uint16_t rec_size) { struct msgb *msg; - msg = osim_new_apdumsg(0x00, 0xB2, rec_nr, 0x04, 0, rec_size); + msg = osim_new_apdumsg(g_class, 0xB2, rec_nr, 0x04, 0, rec_size); osim_transceive_apdu(st, msg); @@ -101,7 +117,7 @@ static struct msgb *read_binary(struct osim_chan_hdl *st, uint16_t offset, uint1 if (offset > 0x7fff || len > 256) return NULL; - msg = osim_new_apdumsg(0x00, 0xB0, offset >> 8, offset & 0xff, 0, len & 0xff); + msg = osim_new_apdumsg(g_class, 0xB0, offset >> 8, offset & 0xff, 0, len & 0xff); osim_transceive_apdu(st, msg); @@ -175,39 +191,111 @@ static int osim_fcp_fd_decode(struct osim_fcp_fd_decoded *ofd, const uint8_t *fc return 0; } -extern struct osim_card_profile *osim_cprof_usim(void *ctx); +/* TS 51.011 Section 9.3 Type of File */ +static const enum osim_file_type sim2ftype[8] = { + [1] = TYPE_MF, + [2] = TYPE_DF, + [4] = TYPE_EF, +}; + +/* TS 51.011 Section 9.3 Structure of File */ +static const enum osim_ef_type sim2eftype[8] = { + [0] = EF_TYPE_TRANSP, + [1] = EF_TYPE_RECORD_FIXED, + [3] = EF_TYPE_RECORD_CYCLIC, +}; -static struct msgb *try_select_adf_usim(struct osim_chan_hdl *st) +/* TS 51.011 Section 9.2.1 */ +static int osim_fcp_fd_decode_sim(struct osim_fcp_fd_decoded *ofd, const uint8_t *fcp, int fcp_len) +{ + memset(ofd, 0, sizeof(*ofd)); + + if (fcp_len < 14) + return -EINVAL; + + ofd->type = sim2ftype[fcp[6] & 7]; + switch (ofd->type) { + case TYPE_EF: + ofd->ef_type = sim2eftype[fcp[13] & 7]; + if (fcp_len < 13 + fcp[12]) + return -EINVAL; + switch (ofd->ef_type) { + case EF_TYPE_RECORD_FIXED: + case EF_TYPE_RECORD_CYCLIC: + if (fcp_len < 15) + return -EINVAL; + ofd->rec_len = fcp[14]; + ofd->num_rec = ntohs(*(uint16_t *)(fcp+2)) / ofd->rec_len; + break; + default: + break; + } + break; + case TYPE_MF: + case TYPE_DF: + if (fcp_len < 22) + return -EINVAL; + break; + default: + break; + } + + return 0; +} + +/*! scan an UICC for all installed apps; allocate osim_card_app_hdl for each of them */ +static int osim_uicc_scan_apps(struct osim_chan_hdl *st) { struct tlv_parsed tp; struct osim_fcp_fd_decoded ofd; - struct msgb *msg, *msg2; + struct msgb *msg; uint8_t *cur; int rc, i; + /* we don't know where we currently might be; go back to MF */ + msg = select_file(st, 0x3f00); + if (!msg) + return -EIO; + if (msgb_apdu_sw(msg) != 0x9000) + return -msgb_apdu_sw(msg); + + /* select EF.DIR */ msg = select_file(st, 0x2f00); + if (!msg) + return -EIO; + /* return status word in case of error */ + if (msgb_apdu_sw(msg) != 0x9000) + return -msgb_apdu_sw(msg); + + /* various FCP related sanity checks */ rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0); - if (rc < 0) - return NULL; + if (rc < 0) { + fprintf(stderr, "Error decoding EF.DIR FCP TLV\n"); + msgb_free(msg); + return -EINVAL; + } dump_fcp_template(&tp); if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) || TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 5) { + fprintf(stderr, "No EF.DIR FCP file description\n"); msgb_free(msg); - return NULL; + return -EINVAL; } rc = osim_fcp_fd_decode(&ofd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC), TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC)); if (rc < 0) { + fprintf(stderr, "Error decoding EF.DIR FCP file description\n"); msgb_free(msg); - return NULL; + return -EINVAL; } if (ofd.type != TYPE_EF || ofd.ef_type != EF_TYPE_RECORD_FIXED) { + fprintf(stderr, "EF.DIR is not a fixed record EF!?!\n"); msgb_free(msg); - return NULL; + return -EINVAL; } msgb_free(msg); @@ -215,109 +303,147 @@ static struct msgb *try_select_adf_usim(struct osim_chan_hdl *st) printf("ofd rec_len = %u, num_rec = %u\n", ofd.rec_len, ofd.num_rec); for (i = 0; i < ofd.num_rec; i++) { + const uint8_t *aid; + uint8_t aid_len; msg = read_record_nr(st, i+1, ofd.rec_len); - if (!msg) - return NULL; + if (!msg) { + fprintf(stderr, "Error reading Record %u of EF.DIR, skipping\n", i+1); + continue; + } + + /* Entries look like this: + * 61194f10 a0000000871002ffffffff8907090000 5005 5553696d31 ffffffffffffffffffffff */ cur = msgb_apdu_de(msg); if (msgb_apdu_le(msg) < 5) { + fprintf(stderr, "Record length %u too short for EF.DIR, skipping\n", msgb_apdu_le(msg)); msgb_free(msg); - return NULL; + continue; } if (cur[0] != 0x61 || cur[1] < 0x03 || cur[1] > 0x7f || cur[2] != 0x4F || cur[3] < 0x01 || cur[3] > 0x10) { + fprintf(stderr, "Unexpected/unknown record in EF.DIR: %s, skipping\n", + osmo_hexdump_nospc(msgb_apdu_de(msg), msgb_apdu_le(msg))); msgb_free(msg); - return NULL; + continue; } + aid_len = cur[3]; + aid = cur+4; - /* FIXME: actually check if it is an AID that we support, or - * iterate until we find one that we support */ - - msg2 = select_adf(st, cur+4, cur[3]); - - /* attach the USIM profile, FIXME: do this based on AID match */ - st->card->prof = osim_cprof_usim(st->card); - st->cwd = osim_file_desc_find_name(st->card->prof->mf, "ADF.USIM"); - - msgb_free(msg); - - return msg2; + /* FIXME: parse / pass label*/ + printf("Detected AID %s\n", osmo_hexdump_nospc(aid, aid_len)); + osim_card_hdl_add_app(st->card, aid, aid_len, NULL); } - return NULL; + return i; } -static int dump_file(struct osim_chan_hdl *chan, uint16_t fid) + +extern struct osim_card_profile *osim_cprof_sim(void *ctx); +extern struct osim_card_profile *osim_cprof_uicc(void *ctx, bool have_df_gsm); + +static int dump_file(struct osim_chan_hdl *chan, const char *short_name, uint16_t fid) { struct tlv_parsed tp; struct osim_fcp_fd_decoded ffdd; struct msgb *msg, *rmsg; int rc, i, offset; + FILE *f_data = NULL; + /* Select the file */ msg = select_file(chan, fid); if (!msg) { - printf("Unable to select file\n"); + fprintf(stderr, "Unable to select file\n"); return -EIO; } - printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); if (msgb_apdu_sw(msg) != 0x9000) { - printf("status 0x%04x selecting file\n", msgb_apdu_sw(msg)); + fprintf(stderr, "status 0x%04x selecting file\n", msgb_apdu_sw(msg)); goto out; } - rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0); - if (rc < 0) { - printf("Unable to parse FCP\n"); - goto out; - } + if (g_class != 0xA0) { + rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0); + if (rc < 0) { + fprintf(stderr, "Unable to parse FCP: %s\n", msgb_hexdump(msg)); + goto out; + } - if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) || - TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 2) { - printf("No file descriptor present ?!?\n"); - goto out; + if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) || + TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 2) { + fprintf(stderr, "No file descriptor present ?!?\n"); + goto out; + } + + rc = osim_fcp_fd_decode(&ffdd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC), + TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC)); + } else { + rc = osim_fcp_fd_decode_sim(&ffdd, msgb_apdu_de(msg), msgb_apdu_le(msg)); } - rc = osim_fcp_fd_decode(&ffdd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC), - TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC)); if (rc < 0) { - printf("Unable to decode File Descriptor\n"); + fprintf(stderr, "Unable to decode File Descriptor\n"); goto out; } if (ffdd.type != TYPE_EF) { - printf("File Type != EF\n"); + fprintf(stderr, "File Type != EF\n"); goto out; } + if (g_output_dir) { + f_data = fopen(short_name, "w"); + if (!f_data) { + fprintf(stderr, "Couldn't create '%s': %s\n", short_name, strerror(errno)); + goto out; + } + } + printf("EF type: %u\n", ffdd.ef_type); switch (ffdd.ef_type) { case EF_TYPE_RECORD_FIXED: for (i = 0; i < ffdd.num_rec; i++) { + const char *hex; rmsg = read_record_nr(chan, i+1, ffdd.rec_len); - if (!rmsg) + if (!rmsg) { + if (f_data) + fclose(f_data); return -EIO; + } printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); - printf("Rec %03u: %s\n", i+1, - osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg))); + + hex = osmo_hexdump_nospc(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg)); + printf("Rec %03u: %s\n", i+1, hex); + if (f_data) + fprintf(f_data, "%s\n", hex); } break; case EF_TYPE_TRANSP: - if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_SIZE)) + if (g_class != 0xA0) { + if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_SIZE)) + goto out; + i = ntohs(*(uint16_t *)TLVP_VAL(&tp, UICC_FCP_T_FILE_SIZE)); + printf("File size: %d bytes\n", i); + } else { + fprintf(stderr, "Can not determine file size, invalid EF-type!\n"); goto out; - i = ntohs(*(uint16_t *)TLVP_VAL(&tp, UICC_FCP_T_FILE_SIZE)); - printf("File size: %d bytes\n", i); - + } for (offset = 0; offset < i-1; ) { uint16_t remain_len = i - offset; uint16_t read_len = OSMO_MIN(remain_len, 256); + const char *hex; rmsg = read_binary(chan, offset, read_len); - if (!rmsg) + if (!rmsg) { + if (f_data) + fclose(f_data); return -EIO; + } offset += read_len; - printf("Content: %s\n", - osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg))); + hex = osmo_hexdump_nospc(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg)); + printf("Content: %s\n", hex); + if (f_data) + fprintf(f_data, "%s", hex); } break; default: @@ -325,8 +451,11 @@ static int dump_file(struct osim_chan_hdl *chan, uint16_t fid) } out: + if (f_data) + fclose(f_data); msgb_free(msg); return -EINVAL; + } static void print_help(void) @@ -334,6 +463,7 @@ static void print_help(void) printf( "osmo-sim-test Usage:\n" " -h --help This message\n" " -n --reader-num NR Open reader number NR\n" + " -o --output-dir DIR To-be-created output directory for filesystem dump\n" ); } @@ -346,10 +476,11 @@ static void handle_options(int argc, char **argv) const struct option long_options[] = { { "help", 0, 0, 'h' }, { "reader-num", 1, 0, 'n' }, + { "output-dir", 1, 0, 'o' }, {0,0,0,0} }; - c = getopt_long(argc, argv, "hn:", + c = getopt_long(argc, argv, "hn:o:", long_options, &option_index); if (c == -1) break; @@ -362,6 +493,9 @@ static void handle_options(int argc, char **argv) case 'n': readernum = atoi(optarg); break; + case 'o': + g_output_dir = optarg; + break; default: exit(2); break; @@ -374,15 +508,133 @@ static void handle_options(int argc, char **argv) } } + +static void mkdir_and_chdir(const char *name, mode_t mode) +{ + int rc; + rc = mkdir(name, mode); + if (rc < 0) { + fprintf(stderr, "Cannot create '%s': %s\n", name, strerror(errno)); + exit(24); + } + rc = chdir(name); + if (rc < 0) { + fprintf(stderr, "Cannot change to just-created '%s': %s\n", name, strerror(errno)); + exit(24); + } +} + + +static void iterate_fs(struct osim_chan_hdl *chan) +{ + const struct osim_file_desc *prev_cwd; + struct osim_file_desc *ofd; + + /* iterate over all files in current working directory */ + llist_for_each_entry(ofd, &chan->cwd->child_list, list) { + struct msgb *m; + char prev_dir[PATH_MAX]; + + printf("\n\n================ %s (%s) ==================\n", + ofd->short_name, ofd->long_name); + + m = select_file(chan, ofd->fid); + if (msgb_apdu_sw(m) != 0x9000) { + msgb_free(m); + continue; + } + dump_fcp_template_msg(m); + msgb_free(m); + + /* If this is a DF, recurse into it */ + switch (ofd->type) { + case TYPE_DF: + /* the select above has just changed into this directory */ + prev_cwd = chan->cwd; + chan->cwd = ofd; + if (g_output_dir) { + if (!getcwd(prev_dir, sizeof(prev_dir))) { + fprintf(stderr, "Cannot determine cwd: %s\n", strerror(errno)); + exit(23); + continue; + } + mkdir_and_chdir(ofd->short_name, 0750); + } + iterate_fs(chan); + /* "pop" the directory from the stack */ + chan->cwd = prev_cwd; + if (g_output_dir) + OSMO_ASSERT(chdir("..") == 0); + break; + default: + dump_file(chan, ofd->short_name, ofd->fid); + break; + } + } +} + +static void iterate_apps(struct osim_chan_hdl *chan) +{ + struct osim_card_app_hdl *cah; + + llist_for_each_entry(cah, &chan->card->apps, list) { + const struct osim_card_app_profile *cap = cah->prof; + struct msgb *msg; + + if (!cap) { + fprintf(stderr, "Unknown AID %s; skipping\n", + osmo_hexdump_nospc(cah->aid, cah->aid_len)); + continue; + } + + msg = select_adf(chan, cah->aid, cah->aid_len); + if (!msg) { + fprintf(stderr, "Error selectiong ADF for AID %s; skipping\n", + osmo_hexdump_nospc(cah->aid, cah->aid_len)); + continue; + } + printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); + chan->cur_app = cah; + chan->cwd = cap->adf; + + if (g_output_dir) + mkdir_and_chdir(cap->adf->short_name, 0750); + + iterate_fs(chan); + + if (g_output_dir) + OSMO_ASSERT(chdir("..") == 0); + } +} + + int main(int argc, char **argv) { struct osim_reader_hdl *reader; struct osim_card_hdl *card; struct osim_chan_hdl *chan; - struct msgb *msg; + int rc; handle_options(argc, argv); + osim_init(NULL); + + if (g_output_dir) { + int rc; + rc = mkdir(g_output_dir, 0750); + if (rc < 0) { + fprintf(stderr, "Cannot create directory '%s': %s\n", g_output_dir, + strerror(errno)); + exit(5); + } + rc = chdir(g_output_dir); + if (rc < 0) { + fprintf(stderr, "Cannot change to just-created directory '%s': %s\n", + g_output_dir, strerror(errno)); + exit(5); + } + } + reader = osim_reader_open(OSIM_READER_DRV_PCSC, readernum, "", NULL); if (!reader) exit(1); @@ -393,36 +645,26 @@ int main(int argc, char **argv) if (!chan) exit(3); - msg = try_select_adf_usim(chan); - if (!msg || msgb_apdu_sw(msg) != 0x9000) + //verify_pin(chan, 1, "1653"); + + rc = osim_uicc_scan_apps(chan); + if (rc >= 0) { + chan->card->prof = osim_cprof_uicc(chan->card, true); + chan->cwd = chan->card->prof->mf; + } else if (rc == -0x6e00) { + /* CLA not supported: must be classic SIM, not USIM */ + g_class = 0xA0; + chan->card->prof = osim_cprof_sim(chan->card); + chan->cwd = chan->card->prof->mf; + } else if (rc < 0) { exit(4); - dump_fcp_template_msg(msg); - msgb_free(msg); - - msg = select_file(chan, 0x6fc5); - dump_fcp_template_msg(msg); - printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); - msgb_free(msg); - - verify_pin(chan, 1, "1653"); - - msg = select_file(chan, 0x6f06); - dump_fcp_template_msg(msg); - msgb_free(msg); + } - { - struct osim_file_desc *ofd; - llist_for_each_entry(ofd, &chan->cwd->child_list, list) { - struct msgb *m; - printf("\n\n================ %s (%s) ==================\n", - ofd->short_name, ofd->long_name); + /* first iterate over normal file system */ + iterate_fs(chan); - m = select_file(chan, ofd->fid); - dump_fcp_template_msg(m); - msgb_free(m); - dump_file(chan, ofd->fid); - } - } + /* then itereate over all apps and their file system */ + iterate_apps(chan); exit(0); } |