# HG changeset patch # User Mychaela Falconia # Date 1615705885 0 # Node ID ddd767f6e15b9773adb4d252030f8bdbdfdde956 # Parent c9ef9e91dd8ebb8390893c7564218bf77936d51d fc-simtool ported over diff -r c9ef9e91dd8e -r ddd767f6e15b .hgignore --- a/.hgignore Sun Mar 14 06:55:38 2021 +0000 +++ b/.hgignore Sun Mar 14 07:11:25 2021 +0000 @@ -8,3 +8,5 @@ ^pcsc/fc-pcsc-atr$ ^pcsc/fc-pcsc-backend$ ^pcsc/fc-pcsc-list$ + +^simtool/fc-simtool$ diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/Makefile Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,26 @@ +CC= gcc +CFLAGS= -O2 +CPPFLAGS=-I../libcommon +PROG= fc-simtool +OBJS= a38.o bfsearch.o chv.o chvext.o curfile.o dispatch.o dumpdir.o \ + erasefile.o fplmn.o getresp.o grcard1.o grcard2.o hlread.o \ + inval_rehab.o lndwrite.o main.o miscadm.o opldump.o oplprog.o \ + pbcommon.o pbdump.o pberase.o pbrestore.o pbupd_imm.o pbupd_immhex.o \ + plmnsel.o pnndump.o pnnprog.o readcmd.o readef.o readops.o restorebin.o\ + savebin.o script.o select.o sjs1_hacks.o smserase.o smsp_common.o \ + smsp_dump.o smsp_erase.o smsp_restore.o smsp_set.o sstlist.o sstprog.o \ + stktest.o telsum.o usersum.o writecmd.o writeops.o +LIBS= ../libcommon/libcommon.a ../libutil/libutil.a +INSTBIN=/opt/freecalypso/bin + +all: ${PROG} + +${PROG}: ${OBJS} ${LIBS} + ${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} + +install: + mkdir -p ${INSTBIN} + install -c ${PROG} ${INSTBIN} + +clean: + rm -f ${PROG} *.o diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/a38.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/a38.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,61 @@ +/* + * This module implements the a38 command for exercising + * the SIM's RUN GSM ALGORITHM operation. + */ + +#include +#include +#include +#include +#include "simresp.h" + +cmd_a38(argc, argv, outf) + char **argv; + FILE *outf; +{ + u_char cmd[21]; + int rc; + + /* RUN GSM ALGORITHM command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x88; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 16; + rc = decode_hex_data_from_string(argv[1], cmd + 5, 16, 16); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9F0C) { + fprintf(stderr, + "error or unexpected SW response to RUN GSM ALGO: %04X\n", + sim_resp_sw); + return(-1); + } + /* GET RESPONSE follow-up */ + cmd[1] = 0xC0; + cmd[4] = 12; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW resp to GET RESPONSE: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != 12) { + fprintf(stderr, + "error: GET RESPONSE returned %u bytes, expected 12\n", + sim_resp_data_len); + return(-1); + } + fprintf(outf, "SRES: %02X %02X %02X %02X\n", sim_resp_data[0], + sim_resp_data[1], sim_resp_data[2], sim_resp_data[3]); + fprintf(outf, "Kc: %02X %02X %02X %02X %02X %02X %02X %02X\n", + sim_resp_data[4], sim_resp_data[5], sim_resp_data[6], + sim_resp_data[7], sim_resp_data[8], sim_resp_data[9], + sim_resp_data[10], sim_resp_data[11]); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/bfsearch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/bfsearch.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,230 @@ +/* + * This module implements a brute force search of file ID space at a given + * file system directory level. + */ + +#include +#include +#include +#include +#include "simresp.h" +#include "file_id.h" + +static +parse_skip_ids(argv, array, total) + char **argv; + unsigned *array, total; +{ + unsigned n; + + for (n = 0; n < total; n++) { + if (!isxdigit(argv[n][0]) || !isxdigit(argv[n][1]) || + !isxdigit(argv[n][2]) || !isxdigit(argv[n][3]) || + argv[n][4]) { + fprintf(stderr, "error: argument is not 4-digit hex\n"); + return(-1); + } + array[n] = strtoul(argv[n], 0, 16); + } + return(0); +} + +static void +report_ef_struct(outf) + FILE *outf; +{ + unsigned total_size, record_len; + + fputs("EF, ", outf); + total_size = (sim_resp_data[2] << 8) | sim_resp_data[3]; + switch (sim_resp_data[13]) { + case 0x00: + fprintf(outf, "transparent, length %u\n", total_size); + return; + case 0x01: + fputs("linear fixed, ", outf); + break; + case 0x03: + fputs("cyclic, ", outf); + break; + default: + fprintf(outf, "struct 0x%02X\n", sim_resp_data[13]); + return; + } + if (sim_resp_data_len < 15) { + fprintf(outf, "response struct cut off\n"); + return; + } + record_len = sim_resp_data[14]; + fprintf(outf, "record length %u", record_len); + if (record_len && total_size % record_len == 0) + fprintf(outf, ", %u records", total_size / record_len); + putc('\n', outf); +} + +cmd_bfsearch(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned skip_ids[8], num_skip_ids; + unsigned bfs, n; + int rc; + + num_skip_ids = argc - 1; + rc = parse_skip_ids(argv + 1, skip_ids, num_skip_ids); + if (rc < 0) + return(rc); + rc = elem_select_op(skip_ids[0]); + if (rc < 0) + return(rc); + if (!rc) { + fprintf(stderr, "error: starting file ID 0x%04X not found\n", + skip_ids[0]); + return(-1); + } + for (bfs = 0; bfs <= 0xFFFF; bfs++) { + for (n = 0; n < num_skip_ids; n++) { + if (bfs == skip_ids[n]) + break; + } + if (n < num_skip_ids) + continue; + rc = elem_select_op(bfs); + if (rc < 0) + return(rc); + if (!rc) + continue; + rc = get_response_op(); + if (rc < 0) + return(rc); + fprintf(outf, "%04X: ", bfs); + if (sim_resp_data_len < 14) + fprintf(outf, "too-short response struct\n"); + else { + switch (sim_resp_data[6]) { + case 0x01: + fprintf(outf, "MF\n"); + break; + case 0x02: + fprintf(outf, "DF\n"); + break; + case 0x04: + report_ef_struct(outf); + break; + default: + fprintf(outf, "unknown file type %02X\n", + sim_resp_data[6]); + } + } + rc = elem_select_op(skip_ids[0]); + if (rc < 0) + return(rc); + if (!rc) { + fprintf(stderr, + "reselecting starting file ID 0x%04X not-found error\n", + skip_ids[0]); + return(-1); + } + } + return(0); +} + +static +bfsearch_dir(path, pathlen, siblings, nsiblings, outf) + unsigned *path, pathlen, *siblings, nsiblings; + FILE *outf; +{ + unsigned bfs, n; + unsigned df_children[255], ndfc; + unsigned childpath[8]; + int rc; + + for (n = 0; n < pathlen; n++) { + rc = elem_select_op(path[n]); + if (rc < 0) + return(rc); + if (!rc) { + fprintf(stderr, + "error selecting 0x%04X: file not found\n", + path[n]); + return(-1); + } + } + ndfc = 0; + for (bfs = 0; bfs <= 0xFFFF; bfs++) { + for (n = 0; n < pathlen; n++) { + if (bfs == path[n]) + break; + } + if (n < pathlen) + continue; + for (n = 0; n < nsiblings; n++) { + if (bfs == siblings[n]) + break; + } + if (n < nsiblings) + continue; + rc = elem_select_op(bfs); + if (rc < 0) + return(rc); + if (!rc) + continue; + rc = get_response_op(); + if (rc < 0) + return(rc); + for (n = 0; n < pathlen; n++) + fprintf(outf, "%04X/", path[n]); + fprintf(outf, "%04X: ", bfs); + if (sim_resp_data_len < 14) + fprintf(outf, "too-short response struct\n"); + else { + switch (sim_resp_data[6]) { + case 0x01: + fprintf(outf, "MF\n"); + break; + case 0x02: + fprintf(outf, "DF\n"); + if (ndfc < 255) + df_children[ndfc++] = bfs; + break; + case 0x04: + report_ef_struct(outf); + break; + default: + fprintf(outf, "unknown file type %02X\n", + sim_resp_data[6]); + } + } + rc = elem_select_op(path[pathlen-1]); + if (rc < 0) + return(rc); + if (!rc) { + fprintf(stderr, + "reselecting starting file ID 0x%04X not-found error\n", + path[pathlen-1]); + return(-1); + } + } + if (pathlen >= 8) + return(0); + for (n = 0; n < pathlen; n++) + childpath[n] = path[n]; + for (n = 0; n < ndfc; n++) { + childpath[pathlen] = df_children[n]; + rc = bfsearch_dir(childpath, pathlen + 1, df_children, ndfc, + outf); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_bfsearch_full(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned initpath; + + initpath = FILEID_MF; + return bfsearch_dir(&initpath, 1, &initpath, 1, outf); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/chv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/chv.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,219 @@ +/* + * This module implements the standard set of CHV commands + * for GSM 11.11 SIMs. + */ + +#include +#include +#include "simresp.h" + +cmd_verify_chv(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x20; + cmd[2] = 0x00; + switch (argv[0][10]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in verify-chvN command\n"); + return(-1); + } + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_change_chv(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* CHANGE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x24; + cmd[2] = 0x00; + switch (argv[0][10]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in change-chvN command\n"); + return(-1); + } + cmd[4] = 16; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], cmd + 13); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_disable_chv1(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* DISABLE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x26; + cmd[2] = 0x00; + cmd[3] = 0x01; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_disable_chv1_rpt(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* DISABLE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x26; + cmd[2] = 0x00; + cmd[3] = 0x01; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000 && sim_resp_sw != 0x9808) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_enable_chv1(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* ENABLE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x28; + cmd[2] = 0x00; + cmd[3] = 0x01; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_enable_chv1_rpt(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* ENABLE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x28; + cmd[2] = 0x00; + cmd[3] = 0x01; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000 && sim_resp_sw != 0x9808) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_unblock_chv(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* UNBLOCK CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x2C; + cmd[2] = 0x00; + switch (argv[0][11]) { + case '1': + cmd[3] = 0x00; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in unblock-chvN command\n"); + return(-1); + } + cmd[4] = 16; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], cmd + 13); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/chvext.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/chvext.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,58 @@ +/* + * This module implements some commands for extended (non-standard) + * CHV-like operations which some cards use for ADM access control. + */ + +#include +#include +#include "simresp.h" + +cmd_verify_ext(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x20; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 8; + rc = encode_pin_entry(argv[2], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_verify_hex(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x20; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 8; + rc = decode_hex_data_from_string(argv[2], cmd + 5, 8, 8); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/curfile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/curfile.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,4 @@ +/* these global vars hold info about the currently selected EF */ + +unsigned curfile_total_size, curfile_structure; +unsigned curfile_record_len, curfile_record_count; diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/curfile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/curfile.h Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,4 @@ +/* extern definitions for global vars defined in curfile.c */ + +extern unsigned curfile_total_size, curfile_structure; +extern unsigned curfile_record_len, curfile_record_count; diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/dispatch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/dispatch.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,395 @@ +/* + * This module implements the command dispatch for fc-simtool + */ + +#include +#include +#include +#include +#include + +extern int cmd_a38(); +extern int cmd_apdu(); +extern int cmd_atr(); +extern int cmd_bfsearch(); +extern int cmd_bfsearch_full(); +extern int cmd_cd(); +extern int cmd_change_chv(); +extern int cmd_disable_chv1(); +extern int cmd_disable_chv1_rpt(); +extern int cmd_enable_chv1(); +extern int cmd_enable_chv1_rpt(); +extern int cmd_envelope(); +extern int cmd_envelope_imm(); +extern int cmd_erase_file(); +extern int cmd_exec(); +extern int cmd_exit(); +extern int cmd_fix_sysmo_msisdn(); +extern int cmd_fplmn_dump(); +extern int cmd_fplmn_erase(); +extern int cmd_fplmn_erase_all(); +extern int cmd_fplmn_write(); +extern int cmd_fplmn_write_list(); +extern int cmd_get_response(); +extern int cmd_grcard1_set_adm(); +extern int cmd_grcard1_set_ki(); +extern int cmd_grcard1_set_pin(); +extern int cmd_grcard2_set_adm5(); +extern int cmd_grcard2_set_adm5_hex(); +extern int cmd_grcard2_set_comp128(); +extern int cmd_grcard2_set_ki(); +extern int cmd_grcard2_set_pin(); +extern int cmd_grcard2_set_puk(); +extern int cmd_grcard2_set_super(); +extern int cmd_grcard2_set_super_hex(); +extern int cmd_iccid(); +extern int cmd_imsi(); +extern int cmd_imsi_raw(); +extern int cmd_inval_adn(); +extern int cmd_lnd_dump(); +extern int cmd_lnd_erase(); +extern int cmd_lnd_restore(); +extern int cmd_lnd_write(); +extern int cmd_opl_dump(); +extern int cmd_opl_erase(); +extern int cmd_opl_write(); +extern int cmd_pb_dump(); +extern int cmd_pb_dump_rec(); +extern int cmd_pb_erase(); +extern int cmd_pb_erase_one(); +extern int cmd_pb_erase_range(); +extern int cmd_pb_restore(); +extern int cmd_pb_update(); +extern int cmd_pb_update_imm(); +extern int cmd_pb_update_imm_hex(); +extern int cmd_plmnsel_dump(); +extern int cmd_plmnsel_erase(); +extern int cmd_plmnsel_erase_all(); +extern int cmd_plmnsel_write(); +extern int cmd_plmnsel_write_list(); +extern int cmd_pnn_dump(); +extern int cmd_pnn_erase(); +extern int cmd_pnn_write(); +extern int cmd_readbin(); +extern int cmd_readef(); +extern int cmd_readrec(); +extern int cmd_rehab_adn(); +extern int cmd_rehab_imsi(); +extern int cmd_rehab_loci(); +extern int cmd_restore_file(); +extern int cmd_savebin(); +extern int cmd_save_sms_bin(); +extern int cmd_select(); +extern int cmd_sim_resp(); +extern int cmd_sms_erase_all(); +extern int cmd_sms_erase_one(); +extern int cmd_sms_erase_range(); +extern int cmd_smsp_dump(); +extern int cmd_smsp_erase_all(); +extern int cmd_smsp_erase_one(); +extern int cmd_smsp_erase_range(); +extern int cmd_smsp_restore(); +extern int cmd_smsp_set(); +extern int cmd_smsp_set_tag(); +extern int cmd_spn(); +extern int cmd_sst(); +extern int cmd_telecom_sum(); +extern int cmd_terminal_profile(); +extern int cmd_uicc_dir(); +extern int cmd_unblock_chv(); +extern int cmd_update_bin(); +extern int cmd_update_bin_imm(); +extern int cmd_update_rec(); +extern int cmd_update_rec_fill(); +extern int cmd_update_rec_imm(); +extern int cmd_user_sum(); +extern int cmd_verify_chv(); +extern int cmd_verify_ext(); +extern int cmd_verify_hex(); +extern int cmd_verify_sjs1_adm1(); +extern int cmd_write_acc(); +extern int cmd_write_iccid(); +extern int cmd_write_iccid_sh18(); +extern int cmd_write_iccid_sh19(); +extern int cmd_write_imsi(); +extern int cmd_write_imsi_sh(); +extern int cmd_write_spn(); +extern int cmd_write_sst(); + +extern int current_ef_inval(); +extern int current_ef_rehab(); + +static struct cmdtab { + char *cmd; + int minargs; + int maxargs; + int allow_redir; + int (*func)(); +} cmdtab[] = { + {"a38", 1, 1, 1, cmd_a38}, + {"apdu", 1, 1, 0, cmd_apdu}, + {"atr", 0, 0, 0, cmd_atr}, + {"bfsearch", 1, 18, 1, cmd_bfsearch}, + {"bfsearch-full", 0, 0, 1, cmd_bfsearch_full}, + {"bfsearch-mf", 0, 0, 1, cmd_bfsearch_full}, + {"cd", 1, 1, 0, cmd_cd}, + {"change-chv1", 2, 2, 0, cmd_change_chv}, + {"change-chv2", 2, 2, 0, cmd_change_chv}, + {"change-pin1", 2, 2, 0, cmd_change_chv}, + {"change-pin2", 2, 2, 0, cmd_change_chv}, + {"cur-ef-inval", 0, 0, 0, current_ef_inval}, + {"cur-ef-rehab", 0, 0, 0, current_ef_rehab}, + {"disable-chv1", 1, 1, 0, cmd_disable_chv1}, + {"disable-chv1-rpt", 1, 1, 0, cmd_disable_chv1_rpt}, + {"disable-pin1", 1, 1, 0, cmd_disable_chv1}, + {"disable-pin1-rpt", 1, 1, 0, cmd_disable_chv1_rpt}, + {"enable-chv1", 1, 1, 0, cmd_enable_chv1}, + {"enable-chv1-rpt", 1, 1, 0, cmd_enable_chv1_rpt}, + {"enable-pin1", 1, 1, 0, cmd_enable_chv1}, + {"enable-pin1-rpt", 1, 1, 0, cmd_enable_chv1_rpt}, + {"envelope", 1, 1, 0, cmd_envelope}, + {"envelope-imm", 1, 1, 0, cmd_envelope_imm}, + {"erase-file", 1, 2, 0, cmd_erase_file}, + {"exec", 1, 1, 0, cmd_exec}, + {"exit", 0, 1, 0, cmd_exit}, + {"fix-sysmo-msisdn", 0, 0, 0, cmd_fix_sysmo_msisdn}, + {"fplmn-dump", 0, 0, 1, cmd_fplmn_dump}, + {"fplmn-erase", 1, 2, 0, cmd_fplmn_erase}, + {"fplmn-erase-all", 0, 0, 0, cmd_fplmn_erase_all}, + {"fplmn-write", 2, 2, 0, cmd_fplmn_write}, + {"fplmn-write-list", 1, 1, 0, cmd_fplmn_write_list}, + {"get-response", 1, 1, 1, cmd_get_response}, + {"grcard1-set-adm1", 2, 2, 0, cmd_grcard1_set_adm}, + {"grcard1-set-adm2", 2, 2, 0, cmd_grcard1_set_adm}, + {"grcard1-set-ki", 1, 1, 0, cmd_grcard1_set_ki}, + {"grcard1-set-pin1", 2, 2, 0, cmd_grcard1_set_pin}, + {"grcard1-set-pin2", 2, 2, 0, cmd_grcard1_set_pin}, + {"grcard2-set-adm5", 1, 1, 0, cmd_grcard2_set_adm5}, + {"grcard2-set-adm5-hex", 1, 1, 0, cmd_grcard2_set_adm5_hex}, + {"grcard2-set-comp128", 1, 1, 0, cmd_grcard2_set_comp128}, + {"grcard2-set-ki", 1, 1, 0, cmd_grcard2_set_ki}, + {"grcard2-set-pin1", 1, 1, 0, cmd_grcard2_set_pin}, + {"grcard2-set-pin2", 1, 1, 0, cmd_grcard2_set_pin}, + {"grcard2-set-puk1", 1, 1, 0, cmd_grcard2_set_puk}, + {"grcard2-set-puk2", 1, 1, 0, cmd_grcard2_set_puk}, + {"grcard2-set-super", 1, 1, 0, cmd_grcard2_set_super}, + {"grcard2-set-super-hex", 1, 1, 0, cmd_grcard2_set_super_hex}, + {"iccid", 0, 0, 1, cmd_iccid}, + {"imsi", 0, 0, 1, cmd_imsi}, + {"imsi-raw", 0, 0, 1, cmd_imsi_raw}, + {"inval-adn", 0, 0, 0, cmd_inval_adn}, + {"lnd-dump", 0, 0, 1, cmd_lnd_dump}, + {"lnd-erase", 0, 0, 0, cmd_lnd_erase}, + {"lnd-restore", 1, 1, 0, cmd_lnd_restore}, + {"lnd-write", 1, 2, 0, cmd_lnd_write}, + {"opl-dump", 0, 0, 1, cmd_opl_dump}, + {"opl-erase", 1, 2, 0, cmd_opl_erase}, + {"opl-write", 5, 5, 0, cmd_opl_write}, + {"pb-dump", 1, 1, 1, cmd_pb_dump}, + {"pb-dump-rec", 2, 3, 1, cmd_pb_dump_rec}, + {"pb-erase", 1, 1, 0, cmd_pb_erase}, + {"pb-erase-one", 2, 2, 0, cmd_pb_erase_one}, + {"pb-erase-range", 3, 3, 0, cmd_pb_erase_range}, + {"pb-restore", 2, 2, 0, cmd_pb_restore}, + {"pb-update", 2, 2, 0, cmd_pb_update}, + {"pb-update-imm", 3, 4, 0, cmd_pb_update_imm}, + {"pb-update-imm-hex", 4, 4, 0, cmd_pb_update_imm_hex}, + {"plmnsel-dump", 0, 0, 1, cmd_plmnsel_dump}, + {"plmnsel-erase", 1, 2, 0, cmd_plmnsel_erase}, + {"plmnsel-erase-all", 0, 0, 0, cmd_plmnsel_erase_all}, + {"plmnsel-write", 2, 2, 0, cmd_plmnsel_write}, + {"plmnsel-write-list", 1, 1, 0, cmd_plmnsel_write_list}, + {"pnn-dump", 0, 0, 1, cmd_pnn_dump}, + {"pnn-erase", 1, 2, 0, cmd_pnn_erase}, + {"pnn-write", 2, 3, 0, cmd_pnn_write}, + {"quit", 0, 1, 0, cmd_exit}, + {"readbin", 2, 2, 1, cmd_readbin}, + {"readef", 1, 1, 1, cmd_readef}, + {"readrec", 1, 2, 1, cmd_readrec}, + {"rehab-adn", 0, 0, 0, cmd_rehab_adn}, + {"rehab-imsi", 0, 0, 0, cmd_rehab_imsi}, + {"rehab-loci", 0, 0, 0, cmd_rehab_loci}, + {"restore-file", 2, 2, 0, cmd_restore_file}, + {"savebin", 2, 2, 0, cmd_savebin}, + {"save-sms-bin", 1, 1, 0, cmd_save_sms_bin}, + {"select", 1, 1, 1, cmd_select}, + {"sim-resp", 0, 0, 1, cmd_sim_resp}, + {"sms-erase-all", 0, 0, 0, cmd_sms_erase_all}, + {"sms-erase-one", 1, 1, 0, cmd_sms_erase_one}, + {"sms-erase-range", 2, 2, 0, cmd_sms_erase_range}, + {"smsp-dump", 0, 0, 1, cmd_smsp_dump}, + {"smsp-erase-all", 0, 0, 0, cmd_smsp_erase_all}, + {"smsp-erase-one", 1, 1, 0, cmd_smsp_erase_one}, + {"smsp-erase-range", 2, 2, 0, cmd_smsp_erase_range}, + {"smsp-restore", 1, 1, 0, cmd_smsp_restore}, + {"smsp-set", 2, 6, 0, cmd_smsp_set}, + {"smsp-set-tag", 3, 7, 0, cmd_smsp_set_tag}, + {"spn", 0, 0, 1, cmd_spn}, + {"sst", 0, 0, 1, cmd_sst}, + {"telecom-sum", 0, 0, 0, cmd_telecom_sum}, + {"terminal-profile", 1, 1, 0, cmd_terminal_profile}, + {"uicc-dir", 0, 0, 1, cmd_uicc_dir}, + {"unblock-chv1", 2, 2, 0, cmd_unblock_chv}, + {"unblock-chv2", 2, 2, 0, cmd_unblock_chv}, + {"unblock-pin1", 2, 2, 0, cmd_unblock_chv}, + {"unblock-pin2", 2, 2, 0, cmd_unblock_chv}, + {"update-bin", 2, 2, 0, cmd_update_bin}, + {"update-bin-imm", 2, 2, 0, cmd_update_bin_imm}, + {"update-rec", 2, 2, 0, cmd_update_rec}, + {"update-rec-fill", 2, 2, 0, cmd_update_rec_fill}, + {"update-rec-imm", 2, 2, 0, cmd_update_rec_imm}, + {"user-sum", 0, 0, 1, cmd_user_sum}, + {"verify-chv1", 1, 1, 0, cmd_verify_chv}, + {"verify-chv2", 1, 1, 0, cmd_verify_chv}, + {"verify-ext", 2, 2, 0, cmd_verify_ext}, + {"verify-hex", 2, 2, 0, cmd_verify_hex}, + {"verify-pin1", 1, 1, 0, cmd_verify_chv}, + {"verify-pin2", 1, 1, 0, cmd_verify_chv}, + {"verify-sjs1-adm1", 1, 1, 0, cmd_verify_sjs1_adm1}, + {"write-acc", 1, 1, 0, cmd_write_acc}, + {"write-iccid", 1, 1, 0, cmd_write_iccid}, + {"write-iccid-sh18", 1, 1, 0, cmd_write_iccid_sh18}, + {"write-iccid-sh19", 1, 1, 0, cmd_write_iccid_sh19}, + {"write-imsi", 1, 1, 0, cmd_write_imsi}, + {"write-imsi-sh", 1, 1, 0, cmd_write_imsi_sh}, + {"write-spn", 2, 2, 0, cmd_write_spn}, + {"write-sst", 1, 1, 0, cmd_write_sst}, + {0, 0, 0, 0, 0} +}; + +static FILE * +handle_output_redir(str) + char *str; +{ + char *cp, *fn; + FILE *outf; + + for (cp = str; isspace(*cp); cp++) + ; + if (!*cp || *cp == '#') { + fprintf(stderr, "error: no filename after '>'\n"); + return(0); + } + for (fn = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp && *cp != '#') { + fprintf(stderr, "error: invalid syntax after '>'\n"); + return(0); + } + outf = fopen(fn, "w"); + if (!outf) + perror(fn); + return outf; +} + +simtool_dispatch_cmd(cmd, is_script) + char *cmd; +{ + char *argv[20]; + char *cp, **ap; + struct cmdtab *tp; + FILE *outf; + int rc; + + for (cp = cmd; isspace(*cp); cp++) + ; + if (!*cp || *cp == '#') + return(0); + if (is_script) + printf("Script command: %s\n", cp); + if (*cp == '!') + return system(cp + 1); + argv[0] = cp; + while (*cp && !isspace(*cp)) + cp++; + if (*cp) + *cp++ = '\0'; + for (tp = cmdtab; tp->cmd; tp++) + if (!strcmp(tp->cmd, argv[0])) + break; + if (!tp->func) { + fprintf(stderr, "error: no such command\n"); + return(-1); + } + for (ap = argv + 1; ; ) { + while (isspace(*cp)) + cp++; + if (!*cp || *cp == '#' || *cp == '>') + break; + if (ap - argv - 1 >= tp->maxargs) { + fprintf(stderr, "error: too many arguments\n"); + return(-1); + } + if (*cp == '"') { + *ap++ = ++cp; + for (;;) { + if (!*cp) { +unterm_qstring: fprintf(stderr, + "error: unterminated quoted string\n"); + return(-1); + } + if (*cp == '"') + break; + if (*cp++ == '\\') { + if (!*cp) + goto unterm_qstring; + cp++; + } + } + *cp++ = '\0'; + } else { + *ap++ = cp; + while (*cp && !isspace(*cp)) + cp++; + if (*cp) + *cp++ = '\0'; + } + } + if (ap - argv - 1 < tp->minargs) { + fprintf(stderr, "error: too few arguments\n"); + return(-1); + } + *ap = 0; + if (*cp == '>') { + if (!tp->allow_redir) { + fprintf(stderr, + "error: command does not support output redirection\n"); + return(-1); + } + outf = handle_output_redir(cp + 1); + if (!outf) + return(-1); + } else + outf = stdout; + rc = tp->func(ap - argv, argv, outf); + if (outf != stdout) + fclose(outf); + return rc; +} + +dispatch_ready_argv(argc, argv) + char **argv; +{ + struct cmdtab *tp; + + for (tp = cmdtab; tp->cmd; tp++) + if (!strcmp(tp->cmd, argv[0])) + break; + if (!tp->func) { + fprintf(stderr, "error: no such command\n"); + return(-1); + } + if (argc - 1 > tp->maxargs) { + fprintf(stderr, "error: too many arguments\n"); + return(-1); + } + if (argc - 1 < tp->minargs) { + fprintf(stderr, "error: too few arguments\n"); + return(-1); + } + return tp->func(argc, argv, stdout); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/dumpdir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/dumpdir.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,44 @@ +/* + * This module implements the dump of EF_DIR. + */ + +#include +#include +#include "curfile.h" +#include "file_id.h" + +cmd_uicc_dir(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_DIR); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_DIR is not linear fixed\n"); + return(-1); + } + if (curfile_record_len < 5) { + fprintf(stderr, "error: EF_DIR record length is too short\n"); + return(-1); + } + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + fprintf(outf, "Record #%u:\n", recno); + dump_efdir_record(outf); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/erasefile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/erasefile.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,109 @@ +/* + * This module implements the erase-file command. + */ + +#include +#include +#include +#include +#include +#include +#include "curfile.h" + +static +erase_transparent(fill_byte) +{ + u_char data[255]; + unsigned off, cc; + int rc; + + memset(data, fill_byte, 255); + for (off = 0; off < curfile_total_size; off += cc) { + cc = curfile_total_size - off; + if (cc > 255) + cc = 255; + rc = update_bin_op(off, data, cc); + if (rc < 0) + return(rc); + } + return(0); +} + +static +erase_records(fill_byte) +{ + u_char data[255]; + unsigned recno; + int rc; + + memset(data, fill_byte, curfile_record_len); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, data, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} + +static +erase_cyclic(fill_byte) +{ + u_char data[255]; + unsigned count; + int rc; + + memset(data, fill_byte, curfile_record_len); + for (count = 0; count < curfile_record_count; count++) { + rc = update_rec_op(0, 0x03, data, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_erase_file(argc, argv) + char **argv; +{ + int file_id, rc; + unsigned fill_byte; + + if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) && + isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4]) + file_id = strtoul(argv[1], 0, 16); + else + file_id = find_symbolic_file_name(argv[1]); + if (file_id < 0) { + fprintf(stderr, +"error: file ID argument is not a hex value or a recognized symbolic name\n"); + return(-1); + } + rc = select_op(file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (argc > 2) { + fill_byte = strtoul(argv[2], 0, 16); + if (fill_byte > 0xFF) { + fprintf(stderr, "error: invalid fill byte argument\n"); + return(-1); + } + } else + fill_byte = 0xFF; + switch (curfile_structure) { + case 0x00: + /* transparent */ + rc = erase_transparent(fill_byte); + break; + case 0x01: + /* record-based */ + rc = erase_records(fill_byte); + break; + case 0x03: + /* cyclic */ + rc = erase_cyclic(fill_byte); + break; + } + return(rc); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/fplmn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/fplmn.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,161 @@ +/* + * This module implements commands for working with EF_FPLMN. + */ + +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +select_ef_fplmn() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_FPLMN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00) { + fprintf(stderr, "error: EF_FPLMN is not transparent\n"); + return(-1); + } + if (curfile_total_size != 12) { + fprintf(stderr, + "error: EF_FPLMN size does not equal 12 bytes\n"); + return(-1); + } + return(0); +} + +cmd_fplmn_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + u_char *dp; + char ascbuf[8]; + unsigned idx; + + rc = select_ef_fplmn(); + if (rc < 0) + return(rc); + rc = readbin_op(0, 12); + if (rc < 0) + return(rc); + dp = sim_resp_data; + for (idx = 0; idx < 4; idx++, dp += 3) { + if (idx) + putc(' ', outf); + if (dp[0] == 0xFF && dp[1] == 0xFF && dp[2] == 0xFF) + fputs("-blank-", outf); + else { + decode_plmn_3bytes(dp, ascbuf, 1); + fputs(ascbuf, outf); + } + } + putc('\n', outf); + return(0); +} + +cmd_fplmn_write(argc, argv) + char **argv; +{ + int rc; + unsigned idx; + u_char rec[3]; + + rc = select_ef_fplmn(); + if (rc < 0) + return(rc); + idx = strtoul(argv[1], 0, 0); + if (idx >= 4) { + fprintf(stderr, "error: specified index is out of range\n"); + return(-1); + } + rc = encode_plmn_3bytes(argv[2], rec); + if (rc < 0) { + fprintf(stderr, "error: invalid MCC-MNC argument\n"); + return(rc); + } + return update_bin_op(idx * 3, rec, 3); +} + +cmd_fplmn_write_list(argc, argv) + char **argv; +{ + int rc; + u_char buf[12]; + + rc = select_ef_fplmn(); + if (rc < 0) + return(rc); + rc = read_plmn_list_from_file(argv[1], buf, 12); + if (rc < 0) + return(rc); + return update_bin_op(0, buf, 12); +} + +cmd_fplmn_erase(argc, argv) + char **argv; +{ + int rc; + unsigned idx, start, end; + u_char rec[3]; + + rc = select_ef_fplmn(); + if (rc < 0) + return(rc); + start = strtoul(argv[1], 0, 0); + if (start >= 4) { + fprintf(stderr, + "error: specified starting index is out of range\n"); + return(-1); + } + if (!argv[2]) + end = start; + else if (!strcmp(argv[2], "end")) + end = 3; + else { + end = strtoul(argv[1], 0, 0); + if (end >= 4) { + fprintf(stderr, + "error: specified ending index is out of range\n"); + return(-1); + } + if (start > end) { + fprintf(stderr, + "error: reverse index range specified\n"); + return(-1); + } + } + memset(rec, 0xFF, 3); + for (idx = start; idx <= end; idx++) { + rc = update_bin_op(idx * 3, rec, 3); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_fplmn_erase_all(argc, argv) + char **argv; +{ + int rc; + u_char ffbuf[12]; + + rc = select_ef_fplmn(); + if (rc < 0) + return(rc); + memset(ffbuf, 0xFF, 12); + return update_bin_op(0, ffbuf, 12); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/getresp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/getresp.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,69 @@ +/* + * This module implements an elementary GET RESPONSE command + */ + +#include +#include +#include +#include "simresp.h" + +cmd_get_response(argc, argv, outf) + char **argv; + FILE *outf; +{ + u_char cmd[5]; + int rc; + unsigned len; + + len = strtoul(argv[1], 0, 0); + if (len < 1 || len > 256) { + fprintf(stderr, "error: length argument is out of range\n"); + return(-1); + } + /* GET RESPONSE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xC0; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW resp to GET RESPONSE: %04X\n", + sim_resp_sw); + return(-1); + } + display_sim_resp_in_hex(outf); + return(0); +} + +get_response_op() +{ + u_char cmd[5]; + int rc; + unsigned expect_resp_len; + + expect_resp_len = sim_resp_sw & 0xFF; + /* GET RESPONSE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xC0; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = expect_resp_len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW resp to GET RESPONSE: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != expect_resp_len) { + fprintf(stderr, + "error: GET RESPONSE returned %u bytes, expected %u\n", + sim_resp_data_len, expect_resp_len); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/grcard1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/grcard1.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,113 @@ +/* + * This module implements a few special commands for those very few + * incredibly lucky people on Earth who have no-longer-available + * sysmoSIM-GR1 cards, or any other branded variant of the same card + * from Grcard. + */ + +#include +#include +#include "simresp.h" + +cmd_grcard1_set_pin(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* Grcard1 proprietary command APDU */ + cmd[0] = 0x80; + cmd[1] = 0xD4; + cmd[2] = 0x00; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in grcard1-set-pinN command\n"); + return(-1); + } + cmd[4] = 16; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], cmd + 13); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard1_set_adm(argc, argv) + char **argv; +{ + u_char cmd[23]; + int rc; + + /* Grcard1 proprietary command APDU */ + cmd[0] = 0x80; + cmd[1] = 0xD4; + cmd[2] = 0x01; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x04; + break; + case '2': + cmd[3] = 0x05; + break; + default: + fprintf(stderr, "BUG in grcard1-set-admN command\n"); + return(-1); + } + cmd[4] = 18; + cmd[5] = 0x03; + cmd[6] = 0x00; + rc = encode_pin_entry(argv[1], cmd + 7); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], cmd + 15); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 23); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard1_set_ki(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* Grcard1 proprietary command APDU */ + cmd[0] = 0x80; + cmd[1] = 0xD4; + cmd[2] = 0x02; + cmd[3] = 0x00; + cmd[4] = 16; + rc = decode_hex_data_from_string(argv[1], cmd + 5, 16, 16); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/grcard2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/grcard2.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,246 @@ +/* + * This module implements special commands for the grcard.cn card model + * which is known in the Osmocom community as GrcardSIM2: + * + * https://osmocom.org/projects/cellular-infrastructure/wiki/GrcardSIM2 + * + * The sample cards which Mother Mychaela received from Grcard in 2021-02 + * are GrcardSIM2, and so are historical sysmoSIM-GR2 and 30C3 cards. + */ + +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +cmd_grcard2_set_pin(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in grcard2-set-pinN command\n"); + return(-1); + } + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard2_set_puk(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3B; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x00; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in grcard2-set-pukN command\n"); + return(-1); + } + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard2_set_adm5(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + cmd[3] = 0x05; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard2_set_adm5_hex(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + cmd[3] = 0x05; + cmd[4] = 8; + rc = decode_hex_data_from_string(argv[1], cmd + 5, 8, 8); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard2_set_super(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + cmd[3] = 0x0B; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_grcard2_set_super_hex(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + cmd[3] = 0x0B; + cmd[4] = 8; + rc = decode_hex_data_from_string(argv[1], cmd + 5, 8, 8); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +static +select_ef_weki() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(0x0001); /* proprietary EF */ + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 35) { + fprintf(stderr, + "error: EF_WEKI is not a transparent EF of 35 bytes\n"); + return(-1); + } + return(0); +} + +cmd_grcard2_set_comp128(argc, argv) + char **argv; +{ + int rc; + unsigned code; + u_char magic_byte; + + if (argv[1][0] < '1' || argv[1][0] > '3' || argv[1][1]) { + fprintf(stderr, "error: invalid argument\n"); + return(-1); + } + code = argv[1][0] - '1'; + rc = select_ef_weki(); + if (rc < 0) + return(rc); + rc = readbin_op(2, 1); + if (rc < 0) + return(rc); + magic_byte = sim_resp_data[0]; + magic_byte &= 0xFC; + magic_byte |= code; + return update_bin_op(2, &magic_byte, 1); +} + +cmd_grcard2_set_ki(argc, argv) + char **argv; +{ + u_char ki[16]; + int rc; + + rc = decode_hex_data_from_string(argv[1], ki, 16, 16); + if (rc < 0) + return(rc); + rc = select_ef_weki(); + if (rc < 0) + return(rc); + return update_bin_op(3, ki, 16); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/hlread.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/hlread.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,149 @@ +/* + * This module implements some high-level or user-friendly read commands. + */ + +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +cmd_iccid(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + char buf[21], *cp; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_ICCID); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 10) { + fprintf(stderr, "error: expected transparent EF of 10 bytes\n"); + return(-1); + } + rc = readbin_op(0, 10); + if (rc < 0) + return(rc); + decode_reversed_nibbles(sim_resp_data, 10, buf); + for (cp = buf + 20; (cp > buf + 1) && (cp[-1] == 'F'); cp--) + ; + *cp = '\0'; + fprintf(outf, "%s\n", buf); + return(0); +} + +cmd_imsi(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + char buf[17], *endp; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_IMSI); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 9) { + fprintf(stderr, "error: expected transparent EF of 9 bytes\n"); + return(-1); + } + rc = readbin_op(0, 9); + if (rc < 0) + return(rc); + if (sim_resp_data[0] < 1 || sim_resp_data[0] > 8) { +malformed: fprintf(stderr, "error: malformed EF_IMSI record\n"); + return(-1); + } + decode_reversed_nibbles(sim_resp_data + 1, sim_resp_data[0], buf); + endp = buf + (sim_resp_data[0] << 1); + switch (buf[0]) { + case '1': + if (sim_resp_data[0] == 1) + goto malformed; + *--endp = '\0'; + break; + case '9': + *endp = '\0'; + break; + default: + goto malformed; + } + fprintf(outf, "%s\n", buf + 1); + return(0); +} + +cmd_imsi_raw(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + char buf[17]; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_IMSI); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 9) { + fprintf(stderr, "error: expected transparent EF of 9 bytes\n"); + return(-1); + } + rc = readbin_op(0, 9); + if (rc < 0) + return(rc); + decode_reversed_nibbles(sim_resp_data + 1, 8, buf); + buf[16] = '\0'; + fprintf(outf, "%s parity=%c len=%u\n", buf + 1, buf[0], + sim_resp_data[0]); + return(0); +} + +cmd_spn(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned textlen; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_SPN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 17) { + fprintf(stderr, "error: expected transparent EF of 17 bytes\n"); + return(-1); + } + rc = readbin_op(0, 17); + if (rc < 0) + return(rc); + fprintf(outf, "Display condition: %02X\n", sim_resp_data[0]); + fputs("SPN: ", outf); + rc = validate_alpha_field(sim_resp_data + 1, 16, &textlen); + if (rc >= 0) + print_alpha_field(sim_resp_data + 1, textlen, outf); + else + fputs("malformed alpha field", outf); + putc('\n', outf); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/inval_rehab.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/inval_rehab.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,105 @@ +/* + * This module implements the rarely-used INVALIDATE and REHABILITATE + * SIM protocol commands. + */ + +#include +#include +#include "simresp.h" +#include "file_id.h" + +current_ef_inval() +{ + u_char cmd[5]; + int rc; + + /* INVALIDATE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x04; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 0; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to INVALIDATE: %04X\n", + sim_resp_sw); + return(-1); + } + return(0); +} + +current_ef_rehab() +{ + u_char cmd[5]; + int rc; + + /* REHABILITATE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x44; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 0; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to REHABILITATE: %04X\n", + sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_inval_adn() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_ADN); + if (rc < 0) + return(rc); + return current_ef_inval(); +} + +cmd_rehab_adn() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_ADN); + if (rc < 0) + return(rc); + return current_ef_rehab(); +} + +cmd_rehab_imsi() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_IMSI); + if (rc < 0) + return(rc); + return current_ef_rehab(); +} + +cmd_rehab_loci() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_LOCI); + if (rc < 0) + return(rc); + return current_ef_rehab(); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/lndwrite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/lndwrite.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,52 @@ +/* + * This module implements lnd-write and lnd-erase commands. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" + +cmd_lnd_write(argc, argv) + char **argv; +{ + int rc; + u_char record[255], *fixp; + + rc = select_ef_lnd(); + if (rc < 0) + return(rc); + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + rc = encode_phone_number_arg(argv[1], fixp, 0); + if (rc < 0) + return(rc); + if (argv[2]) { + rc = qstring_arg_to_gsm7(argv[2], record, + curfile_record_len - 14); + if (rc < 0) + return(rc); + } + return update_rec_op(0, 0x03, record, curfile_record_len); +} + +cmd_lnd_erase(argc, argv) + char **argv; +{ + int rc; + u_char record[255]; + unsigned count; + + rc = select_ef_lnd(); + if (rc < 0) + return(rc); + memset(record, 0xFF, curfile_record_len); + for (count = 0; count < curfile_record_count; count++) { + rc = update_rec_op(0, 0x03, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/main.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,38 @@ +#include +#include +#include + +extern char be_reader_name[]; +extern char be_atr_string[]; + +main(argc, argv) + char **argv; +{ + extern int optind; + char command[512]; + int rc; + + parse_global_options(argc, argv); + launch_backend(); + collect_backend_init_strings(); + if (argc > optind) { + rc = dispatch_ready_argv(argc - optind, argv + optind); + if (rc) + exit(1); + else + good_exit(); + } + if (be_reader_name) + printf("Card reader name: %s\n", be_reader_name); + if (be_atr_string) + printf("ATR: %s\n", be_atr_string); + for (;;) { + if (isatty(0)) { + fputs("simtool> ", stdout); + fflush(stdout); + } + if (!fgets(command, sizeof command, stdin)) + good_exit(); + simtool_dispatch_cmd(command, 0); + } +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/miscadm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/miscadm.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,216 @@ +/* + * This module implements write-iccid and write-imsi commands, + * available only in the admin programming phase after authenticating + * with some card-vendor-dependent ADM key. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" +#include "file_id.h" + +static +write_iccid_bin(binrec) + u_char *binrec; +{ + int rc; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_ICCID); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 10) { + fprintf(stderr, + "error: EF_ICCID is not a transparent EF of 10 bytes\n"); + return(-1); + } + return update_bin_op(0, binrec, 10); +} + +cmd_write_iccid(argc, argv) + char **argv; +{ + int rc; + u_char nibbles[20], binrec[10]; + + rc = parse_decimal_string_arg(argv[1], nibbles, 20); + if (rc < 0) + return(rc); + pack_reversed_nibbles(nibbles, binrec, 10); + return write_iccid_bin(binrec); +} + +cmd_write_iccid_sh18(argc, argv) + char **argv; +{ + int rc; + u_char nibbles[20], binrec[10]; + + rc = parse_decimal_shorthand(argv[1], nibbles, 18); + if (rc < 0) + return(rc); + nibbles[18] = compute_iccid_luhn(nibbles); + nibbles[19] = 0xF; + pack_reversed_nibbles(nibbles, binrec, 10); + return write_iccid_bin(binrec); +} + +cmd_write_iccid_sh19(argc, argv) + char **argv; +{ + int rc; + u_char nibbles[20], binrec[10]; + + rc = parse_decimal_shorthand(argv[1], nibbles, 19); + if (rc < 0) + return(rc); + if (nibbles[18] != compute_iccid_luhn(nibbles)) { + fprintf(stderr, "error: Luhn check digit mismatch\n"); + return(-1); + } + nibbles[19] = 0xF; + pack_reversed_nibbles(nibbles, binrec, 10); + return write_iccid_bin(binrec); +} + +static +write_imsi_bin(binrec) + u_char *binrec; +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_IMSI); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 9) { + fprintf(stderr, + "error: EF_IMSI is not a transparent EF of 9 bytes\n"); + return(-1); + } + return update_bin_op(0, binrec, 9); +} + +cmd_write_imsi(argc, argv) + char **argv; +{ + int rc; + u_char nibbles[16], binrec[9]; + unsigned ndig; + + rc = parse_decimal_string_arg(argv[1], nibbles + 1, 15); + if (rc < 0) + return(rc); + ndig = rc; + if (ndig & 1) + nibbles[0] = 9; + else + nibbles[0] = 1; + binrec[0] = (ndig + 2) >> 1; + pack_reversed_nibbles(nibbles, binrec + 1, 8); + return write_imsi_bin(binrec); +} + +cmd_write_imsi_sh(argc, argv) + char **argv; +{ + int rc; + u_char nibbles[16], binrec[9]; + + rc = parse_decimal_shorthand(argv[1], nibbles + 1, 15); + if (rc < 0) + return(rc); + nibbles[0] = 9; + binrec[0] = 8; + pack_reversed_nibbles(nibbles, binrec + 1, 8); + return write_imsi_bin(binrec); +} + +static +write_acc_bin(binrec) + u_char *binrec; +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_ACC); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 2) { + fprintf(stderr, + "error: EF_ACC is not a transparent EF of 2 bytes\n"); + return(-1); + } + return update_bin_op(0, binrec, 2); +} + +cmd_write_acc(argc, argv) + char **argv; +{ + unsigned acc; + u_char binrec[2]; + + acc = strtoul(argv[1], 0, 16); + if (acc > 0xFFFF) { + fprintf(stderr, "error: ACC argument is out of range\n"); + return(-1); + } + binrec[0] = acc >> 8; + binrec[1] = acc; + return write_acc_bin(binrec); +} + +static +write_spn_bin(binrec) + u_char *binrec; +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_SPN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 17) { + fprintf(stderr, + "error: EF_SPN is not a transparent EF of 17 bytes\n"); + return(-1); + } + return update_bin_op(0, binrec, 17); +} + +cmd_write_spn(argc, argv) + char **argv; +{ + u_char binrec[17]; + int rc; + + binrec[0] = strtoul(argv[1], 0, 16); + memset(binrec + 1, 0xFF, 16); + rc = qstring_arg_to_gsm7(argv[2], binrec + 1, 16); + if (rc < 0) + return(rc); + return write_spn_bin(binrec); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/opldump.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/opldump.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,69 @@ +/* + * This module implements the opl-dump command, + * a companion command to pnn-dump. + */ + +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +select_ef_opl() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_OPL); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_OPL is not linear fixed\n"); + return(-1); + } + if (curfile_record_len < 8) { + fprintf(stderr, +"error: EF_OPL record length is less than the spec minimum of 8 bytes\n"); + return(-1); + } + return(0); +} + +static void +dump_record(recno, outf) + unsigned recno; + FILE *outf; +{ + char ascbuf[8]; + + decode_plmn_3bytes(sim_resp_data, ascbuf, 0); + fprintf(outf, "#%u: %s %02X%02X-%02X%02X %u\n", recno, ascbuf, + sim_resp_data[3], sim_resp_data[4], sim_resp_data[5], + sim_resp_data[6], sim_resp_data[7]); +} + +cmd_opl_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno; + + rc = select_ef_opl(); + if (rc < 0) + return(rc); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + dump_record(recno, outf); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/oplprog.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/oplprog.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,84 @@ +/* + * This module implements functions for admin programming of EF_OPL. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" + +cmd_opl_write(argc, argv) + char **argv; +{ + int rc; + unsigned recno, lac; + u_char record[255]; + + rc = select_ef_opl(); + if (rc < 0) + return(rc); + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + rc = encode_plmn_3bytes(argv[2], record); + if (rc < 0) { + fprintf(stderr, "error: invalid MCC-MNC argument\n"); + return(rc); + } + lac = strtoul(argv[3], 0, 16); + record[3] = lac >> 8; + record[4] = lac; + lac = strtoul(argv[4], 0, 16); + record[5] = lac >> 8; + record[6] = lac; + record[7] = strtoul(argv[5], 0, 0); + if (curfile_record_len > 8) + memset(record + 8, 0xFF, curfile_record_len - 8); + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_opl_erase(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + u_char record[255]; + + rc = select_ef_opl(); + if (rc < 0) + return(rc); + startrec = strtoul(argv[1], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (!argv[2]) + endrec = startrec; + else if (!strcmp(argv[2], "end")) + endrec = curfile_record_count; + else { + endrec = strtoul(argv[2], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } + memset(record, 0xFF, curfile_record_len); + for (recno = startrec; recno <= endrec; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pbcommon.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbcommon.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,100 @@ +/* + * This module implements the common functions for all phonebook commands. + */ + +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static struct map { + char *user_name; + char *canon_name; + int dir_id; + int file_id; +} phonebook_map[] = { + {"adn", "EF_ADN", DF_TELECOM, EF_ADN}, + {"ADN", "EF_ADN", DF_TELECOM, EF_ADN}, + {"EF_ADN", "EF_ADN", DF_TELECOM, EF_ADN}, + {"fdn", "EF_FDN", DF_TELECOM, EF_FDN}, + {"FDN", "EF_FDN", DF_TELECOM, EF_FDN}, + {"EF_FDN", "EF_FDN", DF_TELECOM, EF_FDN}, + {"sdn", "EF_SDN", DF_TELECOM, EF_SDN}, + {"SDN", "EF_SDN", DF_TELECOM, EF_SDN}, + {"EF_SDN", "EF_SDN", DF_TELECOM, EF_SDN}, + {"msisdn", "EF_MSISDN", DF_TELECOM, EF_MSISDN}, + {"MSISDN", "EF_MSISDN", DF_TELECOM, EF_MSISDN}, + {"EF_MSISDN", "EF_MSISDN", DF_TELECOM, EF_MSISDN}, + {"mbdn", "EF_MBDN", DF_GSM, EF_MBDN}, + {"MBDN", "EF_MBDN", DF_GSM, EF_MBDN}, + {"EF_MBDN", "EF_MBDN", DF_GSM, EF_MBDN}, + /* table search terminator */ + {0, 0, -1, -1} +}; + +phonebook_op_common(reqname) + char *reqname; +{ + struct map *tp; + int rc; + + for (tp = phonebook_map; tp->user_name; tp++) + if (!strcmp(tp->user_name, reqname)) + break; + if (!tp->canon_name) { + fprintf(stderr, "error: phone book name \"%s\" not known\n", + reqname); + return(-1); + } + rc = select_op(tp->dir_id); + if (rc < 0) + return(rc); + rc = select_op(tp->file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: %s is not linear fixed\n", + tp->canon_name); + return(-1); + } + if (curfile_record_len < 14) { + fprintf(stderr, + "error: %s has record length of %u bytes, less than minimum 14\n", + tp->canon_name, curfile_record_len); + return(-1); + } + return(0); +} + +select_ef_lnd() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_LND); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x03) { + fprintf(stderr, "error: EF_LND is not cyclic\n"); + return(-1); + } + if (curfile_record_len < 14) { + fprintf(stderr, + "error: EF_LND has record length of %u bytes, less than minimum 14\n", + curfile_record_len); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pbdump.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbdump.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,142 @@ +/* + * This module implements pb-dump and pb-dump-rec commands. + */ + +#include +#include +#include +#include "simresp.h" +#include "curfile.h" + +static +check_blank_area(dp, endp) + u_char *dp, *endp; +{ + while (dp < endp) + if (*dp++ != 0xFF) + return(-1); + return(0); +} + +static void +dump_record(recno, outf) + unsigned recno; + FILE *outf; +{ + int rc; + unsigned textlen; + u_char *fixp; + char digits[21]; + + fprintf(outf, "#%u: ", recno); + if (sim_resp_data_len > 14) { + rc = validate_alpha_field(sim_resp_data, + sim_resp_data_len - 14, + &textlen); + if (rc < 0) { +malformed: fprintf(outf, "malformed record\n"); + return; + } + } else + textlen = 0; + fixp = sim_resp_data + sim_resp_data_len - 14; + if (fixp[0] < 2 || fixp[0] > 11) + goto malformed; + rc = decode_phone_number(fixp + 2, fixp[0] - 1, digits); + if (rc < 0) + goto malformed; + rc = check_blank_area(fixp + 1 + fixp[0], fixp + 12); + if (rc < 0) + goto malformed; + /* all checks passed */ + fprintf(outf, "%s,0x%02X ", digits, fixp[1]); + if (fixp[12] != 0xFF) + fprintf(outf, "CCP=%u ", fixp[12]); + if (fixp[13] != 0xFF) + fprintf(outf, "EXT=%u ", fixp[13]); + print_alpha_field(sim_resp_data, textlen, outf); + putc('\n', outf); +} + +cmd_pb_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + dump_record(recno, outf); + } + return(0); +} + +cmd_pb_dump_rec(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno, startrec, endrec; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + startrec = strtoul(argv[2], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (argv[3]) { + endrec = strtoul(argv[3], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } else + endrec = startrec; + for (recno = startrec; recno <= endrec; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + dump_record(recno, outf); + } + return(0); +} + +cmd_lnd_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno; + + rc = select_ef_lnd(); + if (rc < 0) + return(rc); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + dump_record(recno, outf); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pberase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pberase.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,89 @@ +/* + * This module implements the pb-erase family of commands. + */ + +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" + +cmd_pb_erase(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + memset(record, 0xFF, curfile_record_len); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_pb_erase_one(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_pb_erase_range(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + u_char record[255]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + startrec = strtoul(argv[2], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (!strcmp(argv[3], "end")) + endrec = curfile_record_count; + else { + endrec = strtoul(argv[3], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } + memset(record, 0xFF, curfile_record_len); + for (recno = startrec; recno <= endrec; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pbrestore.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbrestore.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,252 @@ +/* + * This module implements pb-restore and pb-update commands. + */ + +#include +#include +#include +#include +#include +#include +#include "curfile.h" + +extern FILE *open_script_input_file(); + +extern char *alpha_from_file_qstring(); +extern char *alpha_from_file_hex(); + +static +process_record(line, bin_file_buf, filename_for_errs, lineno_for_errs) + char *line, *filename_for_errs; + u_char *bin_file_buf; +{ + unsigned recno; + u_char record[255], *fixp; + u_char digits[20]; + unsigned ndigits, num_digit_bytes; + char *cp; + int c; + + recno = strtoul(line+1, 0, 10); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "%s line %d: record number is out of range\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + cp = line + 1; + while (isdigit(*cp)) + cp++; + if (*cp++ != ':') { +inv_syntax: fprintf(stderr, "%s line %d: invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + while (isspace(*cp)) + cp++; + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + if (digit_char_to_gsm(*cp) < 0) + goto inv_syntax; + for (ndigits = 0; ; ndigits++) { + c = digit_char_to_gsm(*cp); + if (c < 0) + break; + cp++; + if (ndigits >= 20) { + fprintf(stderr, "%s line %d: too many number digits\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + digits[ndigits] = c; + } + if (ndigits & 1) + digits[ndigits++] = 0xF; + num_digit_bytes = ndigits >> 1; + fixp[0] = num_digit_bytes + 1; + pack_digit_bytes(digits, fixp + 2, num_digit_bytes); + if (*cp++ != ',') + goto inv_syntax; + if (cp[0] != '0' || cp[1] != 'x' && cp[1] != 'X' || !isxdigit(cp[2]) || + !isxdigit(cp[3]) || !isspace(cp[4])) + goto inv_syntax; + fixp[1] = strtoul(cp, 0, 16); + cp += 5; + while (isspace(*cp)) + cp++; + if (!strncasecmp(cp, "CCP=", 4)) { + cp += 4; + fixp[12] = strtoul(cp, 0, 0); + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (!strncasecmp(cp, "EXT=", 4)) { + cp += 4; + fixp[13] = strtoul(cp, 0, 0); + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (*cp == '"') { + cp++; + cp = alpha_from_file_qstring(cp, record, + curfile_record_len - 14, + filename_for_errs, + lineno_for_errs); + if (!cp) + return(-1); + } else if (!strncasecmp(cp, "HEX", 3)) { + cp += 3; + while (isspace(*cp)) + cp++; + cp = alpha_from_file_hex(cp, record, curfile_record_len - 14, + filename_for_errs, lineno_for_errs); + if (!cp) + return(-1); + } else + goto inv_syntax; + while (isspace(*cp)) + cp++; + if (*cp) + goto inv_syntax; + if (bin_file_buf) { + bcopy(record, bin_file_buf + (recno - 1) * curfile_record_len, + curfile_record_len); + return(0); + } else + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_pb_restore(argc, argv) + char **argv; +{ + int rc; + FILE *inf; + int lineno; + char linebuf[1024]; + u_char *databuf; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + databuf = malloc(curfile_total_size); + if (!databuf) { + perror("malloc for full phonebook EF"); + return(-1); + } + inf = open_script_input_file(argv[2]); + if (!inf) { + perror(argv[2]); + free(databuf); + return(-1); + } + memset(databuf, 0xFF, curfile_total_size); + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { + if (!index(linebuf, '\n')) { + fprintf(stderr, + "%s line %d: too long or missing newline\n", + argv[2], lineno); + fclose(inf); + free(databuf); + return(-1); + } + if (linebuf[0] != '#' || !isdigit(linebuf[1])) + continue; + rc = process_record(linebuf, databuf, argv[2], lineno); + if (rc < 0) { + fclose(inf); + free(databuf); + return(rc); + } + } + fclose(inf); + rc = restore_bin_records(databuf); + free(databuf); + return(rc); +} + +cmd_pb_update(argc, argv) + char **argv; +{ + int rc; + FILE *inf; + int lineno; + char linebuf[1024]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + inf = open_script_input_file(argv[2]); + if (!inf) { + perror(argv[2]); + return(-1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { + if (!index(linebuf, '\n')) { + fprintf(stderr, + "%s line %d: too long or missing newline\n", + argv[2], lineno); + fclose(inf); + return(-1); + } + if (linebuf[0] != '#' || !isdigit(linebuf[1])) + continue; + rc = process_record(linebuf, 0, argv[2], lineno); + if (rc < 0) { + fclose(inf); + return(rc); + } + } + fclose(inf); + return(0); +} + +cmd_lnd_restore(argc, argv) + char **argv; +{ + int rc; + FILE *inf; + int lineno; + char linebuf[1024]; + u_char *databuf; + + rc = select_ef_lnd(); + if (rc < 0) + return(rc); + databuf = malloc(curfile_total_size); + if (!databuf) { + perror("malloc for full EF_LND"); + return(-1); + } + inf = open_script_input_file(argv[1]); + if (!inf) { + perror(argv[1]); + free(databuf); + return(-1); + } + memset(databuf, 0xFF, curfile_total_size); + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { + if (!index(linebuf, '\n')) { + fprintf(stderr, + "%s line %d: too long or missing newline\n", + argv[1], lineno); + fclose(inf); + free(databuf); + return(-1); + } + if (linebuf[0] != '#' || !isdigit(linebuf[1])) + continue; + rc = process_record(linebuf, databuf, argv[1], lineno); + if (rc < 0) { + fclose(inf); + free(databuf); + return(rc); + } + } + fclose(inf); + rc = restore_bin_cyclic(databuf); + free(databuf); + return(rc); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pbupd_imm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbupd_imm.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,39 @@ +/* + * This module implements the pb-update-imm command. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" + +cmd_pb_update_imm(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + rc = encode_phone_number_arg(argv[3], fixp, 0); + if (rc < 0) + return(rc); + if (argv[4]) { + rc = qstring_arg_to_gsm7(argv[4], record, + curfile_record_len - 14); + if (rc < 0) + return(rc); + } + return update_rec_op(recno, 0x04, record, curfile_record_len); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pbupd_immhex.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbupd_immhex.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,66 @@ +/* + * This module implements the pb-update-imm-hex command. + */ + +#include +#include +#include +#include +#include +#include +#include "curfile.h" + +static +decode_alphatag_arg_hex(arg, record, maxlen) + char *arg; + u_char *record; + unsigned maxlen; +{ + unsigned acclen; + + for (acclen = 0; ; acclen++) { + while (isspace(*arg)) + arg++; + if (!*arg) + break; + if (!isxdigit(arg[0]) || !isxdigit(arg[1])) { + fprintf(stderr, "error: invalid hex string input\n"); + return(-1); + } + if (acclen >= maxlen) { + fprintf(stderr, + "error: alpha tag string is longer than SIM limit\n"); + return(-1); + } + record[acclen] = (decode_hex_digit(arg[0]) << 4) | + decode_hex_digit(arg[1]); + arg += 2; + } + return(0); +} + +cmd_pb_update_imm_hex(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + rc = encode_phone_number_arg(argv[3], fixp, 0); + if (rc < 0) + return(rc); + rc = decode_alphatag_arg_hex(argv[4], record, curfile_record_len - 14); + if (rc < 0) + return(rc); + return update_rec_op(recno, 0x04, record, curfile_record_len); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/plmnsel.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/plmnsel.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,189 @@ +/* + * This module implements commands for working with EF_PLMNsel. + */ + +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +select_ef_plmnsel() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_PLMNsel); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00) { + fprintf(stderr, "error: EF_PLMNsel is not transparent\n"); + return(-1); + } + if (curfile_total_size < 24) { + fprintf(stderr, + "error: EF_PLMNsel is shorter than spec minimum of 24 bytes\n"); + return(-1); + } + if (curfile_total_size > 255) { + fprintf(stderr, + "error: EF_PLMNsel is longer than our 255 byte limit\n"); + return(-1); + } + if (curfile_total_size % 3) { + fprintf(stderr, + "error: EF_PLMNsel length is not a multiple of 3 bytes\n"); + return(-1); + } + return(0); +} + +cmd_plmnsel_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc, gap_flag; + u_char *dp, *endp; + char ascbuf[8]; + unsigned idx, linelen; + + rc = select_ef_plmnsel(); + if (rc < 0) + return(rc); + rc = readbin_op(0, curfile_total_size); + if (rc < 0) + return(rc); + dp = sim_resp_data; + endp = sim_resp_data + sim_resp_data_len; + gap_flag = 0; + linelen = 0; + for (idx = 0; dp < endp; idx++, dp += 3) { + if (dp[0] == 0xFF && dp[1] == 0xFF && dp[2] == 0xFF) { + gap_flag = 1; + continue; + } + if (gap_flag) { + if (linelen) { + putc('\n', outf); + linelen = 0; + } + fprintf(outf, "GAP, continuing at index %u:\n", idx); + gap_flag = 0; + } + if (linelen >= 10) { + putc('\n', outf); + linelen = 0; + } + decode_plmn_3bytes(dp, ascbuf, 1); + if (linelen) + putc(' ', outf); + fputs(ascbuf, outf); + linelen++; + } + if (linelen) + putc('\n', outf); + return(0); +} + +cmd_plmnsel_write(argc, argv) + char **argv; +{ + int rc; + unsigned idx; + u_char rec[3]; + + rc = select_ef_plmnsel(); + if (rc < 0) + return(rc); + idx = strtoul(argv[1], 0, 0); + if (idx >= curfile_total_size / 3) { + fprintf(stderr, "error: specified index is out of range\n"); + return(-1); + } + rc = encode_plmn_3bytes(argv[2], rec); + if (rc < 0) { + fprintf(stderr, "error: invalid MCC-MNC argument\n"); + return(rc); + } + return update_bin_op(idx * 3, rec, 3); +} + +cmd_plmnsel_write_list(argc, argv) + char **argv; +{ + int rc; + u_char buf[255]; + + rc = select_ef_plmnsel(); + if (rc < 0) + return(rc); + rc = read_plmn_list_from_file(argv[1], buf, curfile_total_size); + if (rc < 0) + return(rc); + return update_bin_op(0, buf, curfile_total_size); +} + +cmd_plmnsel_erase(argc, argv) + char **argv; +{ + int rc; + unsigned idx, start, end, nrec; + u_char rec[3]; + + rc = select_ef_plmnsel(); + if (rc < 0) + return(rc); + nrec = curfile_total_size / 3; + start = strtoul(argv[1], 0, 0); + if (start >= nrec) { + fprintf(stderr, + "error: specified starting index is out of range\n"); + return(-1); + } + if (!argv[2]) + end = start; + else if (!strcmp(argv[2], "end")) + end = nrec - 1; + else { + end = strtoul(argv[1], 0, 0); + if (end >= nrec) { + fprintf(stderr, + "error: specified ending index is out of range\n"); + return(-1); + } + if (start > end) { + fprintf(stderr, + "error: reverse index range specified\n"); + return(-1); + } + } + memset(rec, 0xFF, 3); + for (idx = start; idx <= end; idx++) { + rc = update_bin_op(idx * 3, rec, 3); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_plmnsel_erase_all(argc, argv) + char **argv; +{ + int rc; + u_char ffbuf[255]; + + rc = select_ef_plmnsel(); + if (rc < 0) + return(rc); + memset(ffbuf, 0xFF, curfile_total_size); + return update_bin_op(0, ffbuf, curfile_total_size); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pnndump.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pnndump.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,117 @@ +/* + * This module implements the pnn-dump command, providing a + * user-accessible way to identify MVNO SIMs. + */ + +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +select_ef_pnn() +{ + int rc; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_PNN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_PNN is not linear fixed\n"); + return(-1); + } + if (curfile_record_len < 3) { + fprintf(stderr, +"error: EF_PNN record length is less than the spec minimum of 3 bytes\n"); + return(-1); + } + return(0); +} + +static void +dump_record(recno, outf) + unsigned recno; + FILE *outf; +{ + u_char *dp, *endp; + char *name_kw; + unsigned ielen, code_byte, nsept; + u_char gsm7_buf[288]; + + fprintf(outf, "#%u:", recno); + dp = sim_resp_data; + endp = sim_resp_data + sim_resp_data_len; + while (dp < endp) { + if (*dp == 0xFF) + break; + switch (*dp++) { + case 0x43: + name_kw = "Ln"; + break; + case 0x45: + name_kw = "Sn"; + break; + default: + fprintf(outf, " unknown-IEI\n"); + return; + } + if (dp >= endp) { + fprintf(outf, " truncated-IE\n"); + return; + } + ielen = *dp++; + if (ielen < 1 || ielen > (endp - dp)) { + fprintf(outf, " bad-length\n"); + return; + } + code_byte = *dp++; + ielen--; + fprintf(outf, " %s=0x%02X", name_kw, code_byte); + if (!ielen) + continue; + putc(',', outf); + if ((code_byte & 0x70) == 0) { + nsept = ielen * 8 / 7; + gsm7_unpack(dp, gsm7_buf, nsept); + dp += ielen; + print_gsm7_string_to_file(gsm7_buf, nsept, outf); + } else { + for (; ielen; ielen--) + fprintf(outf, "%02X", *dp++); + } + } + for (; dp < endp; dp++) { + if (*dp != 0xFF) { + fprintf(outf, " bad-padding\n"); + return; + } + } + putc('\n', outf); +} + +cmd_pnn_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno; + + rc = select_ef_pnn(); + if (rc < 0) + return(rc); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + dump_record(recno, outf); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/pnnprog.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pnnprog.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,111 @@ +/* + * This module implements functions for admin programming of EF_PNN. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" + +static u_char * +add_field(dp, bytes_avail, namearg, type) + u_char *dp; + char *namearg; + unsigned bytes_avail, type; +{ + u_char gsm7_buf[289]; + unsigned nsept, noct; + int rc; + + if (bytes_avail < 4) { + fprintf(stderr, + "error: PNN record is too short for name element\n"); + return(0); + } + rc = qstring_arg_to_gsm7(namearg, gsm7_buf, (bytes_avail-3) * 8 / 7); + if (rc < 0) + return(0); + nsept = rc; + gsm7_buf[nsept] = 0; + noct = (nsept * 7 + 7) / 8; + *dp++ = type; + *dp++ = noct + 1; + *dp++ = 0x80 | (nsept & 7); + gsm7_pack(gsm7_buf, dp, noct); + dp += noct; + return dp; +} + +cmd_pnn_write(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *dp, *endp; + + rc = select_ef_pnn(); + if (rc < 0) + return(rc); + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + dp = record; + endp = record + curfile_record_len; + dp = add_field(dp, endp - dp, argv[2], 0x43); + if (!dp) + return(-1); + if (argv[3]) { + dp = add_field(dp, endp - dp, argv[3], 0x45); + if (!dp) + return(-1); + } + while (dp < endp) + *dp++ = 0xFF; + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_pnn_erase(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + u_char record[255]; + + rc = select_ef_pnn(); + if (rc < 0) + return(rc); + startrec = strtoul(argv[1], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (!argv[2]) + endrec = startrec; + else if (!strcmp(argv[2], "end")) + endrec = curfile_record_count; + else { + endrec = strtoul(argv[2], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } + memset(record, 0xFF, curfile_record_len); + for (recno = startrec; recno <= endrec; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/readcmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/readcmd.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,68 @@ +/* + * This module implements elementary low-level readbin and readrec commands. + */ + +#include +#include +#include +#include "simresp.h" +#include "curfile.h" + +cmd_readbin(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned offset, len; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0xFFFF) { + fprintf(stderr, "error: offset argument is out of range\n"); + return(-1); + } + len = strtoul(argv[2], 0, 0); + if (len < 1 || len > 256) { + fprintf(stderr, "error: length argument is out of range\n"); + return(-1); + } + rc = readbin_op(offset, len); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(outf); + return(0); +} + +cmd_readrec(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned recno, len; + int rc; + + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > 255) { + fprintf(stderr, + "error: record number argument is out of range\n"); + return(-1); + } + if (argv[2]) { + len = strtoul(argv[2], 0, 0); + if (len < 1 || len > 255) { + fprintf(stderr, + "error: length argument is out of range\n"); + return(-1); + } + } else { + if (!curfile_record_len) { + fprintf(stderr, + "error: no current file record length is available\n"); + return(-1); + } + len = curfile_record_len; + } + rc = readrec_op(recno, 0x04, len); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(outf); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/readef.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/readef.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,127 @@ +/* + * This module implements the readef command for dumping the complete + * content of SIM files. + */ + +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" + +static void +hexdump_with_offset(outf, extoff) + FILE *outf; + unsigned extoff; +{ + unsigned off, cc, n, c; + + for (off = 0; off < sim_resp_data_len; off += cc) { + fprintf(outf, "%04X:", extoff + off); + cc = 16; + if (sim_resp_data_len - off < cc) + cc = sim_resp_data_len - off; + for (n = 0; n < 16; n++) { + if (n == 0 || n == 8) + putc(' ', outf); + putc(' ', outf); + if (n < cc) + fprintf(outf, "%02X", sim_resp_data[off + n]); + else { + putc(' ', outf); + putc(' ', outf); + } + } + putc(' ', outf); + putc(' ', outf); + for (n = 0; n < cc; n++) { + c = sim_resp_data[off + n]; + if (c < 0x20 || c > 0x7E) + c = '.'; + putc(c, outf); + } + putc('\n', outf); + } +} + +static +readef_transparent(outf) + FILE *outf; +{ + unsigned off, cc; + int rc; + + for (off = 0; off < curfile_total_size; off += cc) { + cc = curfile_total_size - off; + if (cc > 256) + cc = 256; + rc = readbin_op(off, cc); + if (rc < 0) + return(rc); + hexdump_with_offset(outf, off); + } + return(0); +} + +static +readef_records(outf) + FILE *outf; +{ + unsigned recno; + int rc; + + for (recno = 1; recno <= curfile_record_count; recno++) { + fprintf(outf, "Record #%u:\n", recno); + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(outf); + } + return(0); +} + +cmd_readef(argc, argv, outf) + char **argv; + FILE *outf; +{ + int file_id, rc; + unsigned readlen; + + if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) && + isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4]) + file_id = strtoul(argv[1], 0, 16); + else + file_id = find_symbolic_file_name(argv[1]); + if (file_id < 0) { + fprintf(stderr, +"error: file ID argument is not a hex value or a recognized symbolic name\n"); + return(-1); + } + rc = select_op(file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + show_access_conditions(outf, "UPDATE", sim_resp_data[8] & 0xF); + show_access_conditions(outf, "READ & SEEK", sim_resp_data[8] >> 4); + show_access_conditions(outf, "INCREASE", sim_resp_data[9] >> 4); + show_access_conditions(outf, "INVALIDATE", sim_resp_data[10] & 0xF); + show_access_conditions(outf, "REHABILITATE", sim_resp_data[10] >> 4); + fprintf(outf, "File status: %02X\n", sim_resp_data[11]); + switch (curfile_structure) { + case 0x00: + fprintf(outf, "Transparent EF of %u byte(s)\n", + curfile_total_size); + return readef_transparent(outf); + case 0x01: + fprintf(outf, "%u records of %u bytes (linear fixed)\n", + curfile_record_count, curfile_record_len); + return readef_records(outf); + case 0x03: + fprintf(outf, "%u records of %u bytes (cyclic)\n", + curfile_record_count, curfile_record_len); + return readef_records(outf); + } +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/readops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/readops.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,62 @@ +#include +#include +#include +#include "simresp.h" + +readbin_op(offset, len) + unsigned offset, len; +{ + u_char cmd[5]; + int rc; + + /* READ BINARY command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xB0; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to READ BINARY: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != len) { + fprintf(stderr, + "error: READ BINARY returned %u bytes, expected %u\n", + sim_resp_data_len, len); + return(-1); + } + return(0); +} + +readrec_op(recno, mode, len) + unsigned recno, mode, len; +{ + u_char cmd[5]; + int rc; + + /* READ RECORD command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xB2; + cmd[2] = recno; + cmd[3] = mode; + cmd[4] = len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to READ RECORD: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != len) { + fprintf(stderr, + "error: READ RECORD returned %u bytes, expected %u\n", + sim_resp_data_len, len); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/restorebin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/restorebin.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,136 @@ +/* + * This module implements the restore-file command; this command + * reads binary files previously saved with the savebin command + * and writes the backed-up bits back to the SIM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "curfile.h" + +restore_bin_transparent(data) + u_char *data; +{ + unsigned off, cc; + int rc; + + for (off = 0; off < curfile_total_size; off += cc) { + cc = curfile_total_size - off; + if (cc > 255) + cc = 255; + rc = update_bin_op(off, data + off, cc); + if (rc < 0) + return(rc); + } + return(0); +} + +restore_bin_records(data) + u_char *data; +{ + unsigned recno; + u_char *dp; + int rc; + + dp = data; + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, dp, curfile_record_len); + if (rc < 0) + return(rc); + dp += curfile_record_len; + } + return(0); +} + +restore_bin_cyclic(data) + u_char *data; +{ + unsigned count; + u_char *dp; + int rc; + + dp = data + curfile_total_size; + for (count = 0; count < curfile_record_count; count++) { + dp -= curfile_record_len; + rc = update_rec_op(0, 0x03, dp, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_restore_file(argc, argv) + char **argv; +{ + int file_id, rc, fd; + struct stat st; + u_char *databuf; + + if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) && + isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4]) + file_id = strtoul(argv[1], 0, 16); + else + file_id = find_symbolic_file_name(argv[1]); + if (file_id < 0) { + fprintf(stderr, +"error: file ID argument is not a hex value or a recognized symbolic name\n"); + return(-1); + } + rc = select_op(file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (!curfile_total_size) { + printf("SIM indicates file of zero length, nothing to do\n"); + return(0); + } + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + perror(argv[2]); + return(-1); + } + fstat(fd, &st); + if ((st.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "error: %s is not a regular file\n", argv[2]); + close(fd); + return(-1); + } + if (st.st_size != curfile_total_size) { + fprintf(stderr, + "error: length of %s does not match SIM EF length of %u bytes\n", + argv[2], curfile_total_size); + close(fd); + return(-1); + } + databuf = malloc(curfile_total_size); + if (!databuf) { + perror("malloc for file data"); + close(fd); + return(-1); + } + read(fd, databuf, curfile_total_size); + close(fd); + switch (curfile_structure) { + case 0x00: + /* transparent */ + rc = restore_bin_transparent(databuf); + break; + case 0x01: + /* record-based */ + rc = restore_bin_records(databuf); + break; + case 0x03: + /* cyclic */ + rc = restore_bin_cyclic(databuf); + break; + } + free(databuf); + return(rc); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/savebin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/savebin.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,119 @@ +/* + * This module implements commands for saving SIM file content + * in UNIX host files. + */ + +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +savebin_transparent(outf) + FILE *outf; +{ + unsigned off, cc; + int rc; + + for (off = 0; off < curfile_total_size; off += cc) { + cc = curfile_total_size - off; + if (cc > 256) + cc = 256; + rc = readbin_op(off, cc); + if (rc < 0) + return(rc); + fwrite(sim_resp_data, 1, cc, outf); + } + return(0); +} + +static +savebin_records(outf) + FILE *outf; +{ + unsigned recno; + int rc; + + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + fwrite(sim_resp_data, curfile_record_len, 1, outf); + } + return(0); +} + +cmd_savebin(argc, argv) + char **argv; +{ + int file_id, rc; + FILE *of; + + if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) && + isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4]) + file_id = strtoul(argv[1], 0, 16); + else + file_id = find_symbolic_file_name(argv[1]); + if (file_id < 0) { + fprintf(stderr, +"error: file ID argument is not a hex value or a recognized symbolic name\n"); + return(-1); + } + rc = select_op(file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + of = fopen(argv[2], "w"); + if (!of) { + perror(argv[2]); + return(-1); + } + switch (curfile_structure) { + case 0x00: + /* transparent */ + rc = savebin_transparent(of); + break; + case 0x01: + case 0x03: + /* record-based */ + rc = savebin_records(of); + break; + } + fclose(of); + return(rc); +} + +cmd_save_sms_bin(argc, argv) + char **argv; +{ + int rc; + FILE *of; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_SMS); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01 || curfile_record_len != 176) { + fprintf(stderr, + "error: EF_SMS is not linear fixed with 176-byte records\n"); + return(-1); + } + of = fopen(argv[1], "w"); + if (!of) { + perror(argv[1]); + return(-1); + } + rc = savebin_records(of); + fclose(of); + return(rc); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/script.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/script.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,39 @@ +/* + * This module implements the exec command, which is our scripting facility. + */ + +#include +#include +#include +#include + +extern FILE *open_script_input_file(); + +cmd_exec(argc, argv) + char **argv; +{ + FILE *f; + char linebuf[512], *cp; + int lineno, retval = 0; + + f = open_script_input_file(argv[1]); + if (!f) { + perror(argv[1]); + return(-1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) { + cp = index(linebuf, '\n'); + if (!cp) { + fprintf(stderr, "%s line %d: missing newline\n", + argv[1], lineno); + fclose(f); + return(-1); + } + *cp = '\0'; + retval = simtool_dispatch_cmd(linebuf, 1); + if (retval) + break; + } + fclose(f); + return(retval); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/select.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/select.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" + +elem_select_op(file_id) + unsigned file_id; +{ + u_char cmd[7]; + int rc; + unsigned expect_resp_len; + + /* SELECT command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xA4; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 2; + cmd[5] = file_id >> 8; + cmd[6] = file_id; + rc = apdu_exchange(cmd, 7); + if (rc < 0) + return(rc); + if (sim_resp_sw == 0x9404) + return(0); + if ((sim_resp_sw & 0xFF00) == 0x9F00) + return(1); + fprintf(stderr, + "error or unexpected SW response to SELECT of 0x%04X: %04X\n", + file_id, sim_resp_sw); + return(-1); +} + +select_op(file_id) + unsigned file_id; +{ + u_char cmd[7]; + int rc; + unsigned expect_resp_len; + + /* SELECT command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xA4; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 2; + cmd[5] = file_id >> 8; + cmd[6] = file_id; + rc = apdu_exchange(cmd, 7); + if (rc < 0) + return(rc); + if ((sim_resp_sw & 0xFF00) != 0x9F00) { + fprintf(stderr, + "error or unexpected SW response to SELECT of 0x%04X: %04X\n", + file_id, sim_resp_sw); + return(-1); + } + expect_resp_len = sim_resp_sw & 0xFF; + /* GET RESPONSE follow-up */ + cmd[1] = 0xC0; + cmd[4] = expect_resp_len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, + "bad SW resp to GET RESPONSE after SELECT: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != expect_resp_len) { + fprintf(stderr, + "error: GET RESPONSE after SELECT returned %u bytes, expected %u\n", + sim_resp_data_len, expect_resp_len); + return(-1); + } + return(0); +} + +static void +show_secret_code_status(outf, name, byte) + FILE *outf; + char *name; + unsigned byte; +{ + fprintf(outf, "Status of %s: %s, %u attempts left\n", name, + byte & 0x80 ? "initialized" : "not initialized", + byte & 0x0F); +} + +void +show_access_conditions(outf, oper_name, cond_code) + FILE *outf; + char *oper_name; + unsigned cond_code; +{ + static char *cond_names[16] = + {"ALW", "CHV1", "CHV2", "RFU3", + "ADM4", "ADM5", "ADM6", "ADM7", + "ADM8", "ADM9", "ADM10", "ADM11", + "ADM12", "ADM13", "ADM14", "NEV"}; + + fprintf(outf, "Access condition for %s: %s\n", oper_name, + cond_names[cond_code]); +} + +cmd_select(argc, argv, outf) + char **argv; + FILE *outf; +{ + int file_id, rc; + + if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) && + isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4]) + file_id = strtoul(argv[1], 0, 16); + else + file_id = find_symbolic_file_name(argv[1]); + if (file_id < 0) { + fprintf(stderr, +"error: file ID argument is not a hex value or a recognized symbolic name\n"); + return(-1); + } + rc = select_op(file_id); + if (rc < 0) + return(rc); + if (sim_resp_data_len < 14) { + fprintf(stderr, + "error: response length of %u bytes is too short for any file type\n", + sim_resp_data_len); + return(-1); + } + switch (sim_resp_data[6]) { + case 0x01: + fprintf(outf, "File type: MF\n"); + goto mf_or_df; + case 0x02: + fprintf(outf, "File type: DF\n"); + mf_or_df: + if (sim_resp_data_len < 22) { + fprintf(stderr, + "error: response length of %u bytes is too short for MF/DF\n", + sim_resp_data_len); + return(-1); + } + fprintf(outf, "File characteristics: %02X\n", + sim_resp_data[13]); + fprintf(outf, "Number of DF children: %u\n", sim_resp_data[14]); + fprintf(outf, "Number of EF children: %u\n", sim_resp_data[15]); + fprintf(outf, "Number of secret codes: %u\n", + sim_resp_data[16]); + show_secret_code_status(outf, "PIN1", sim_resp_data[18]); + show_secret_code_status(outf, "PUK1", sim_resp_data[19]); + show_secret_code_status(outf, "PIN2", sim_resp_data[20]); + show_secret_code_status(outf, "PUK2", sim_resp_data[21]); + break; + case 0x04: + fprintf(outf, "File type: EF\n"); + curfile_total_size = (sim_resp_data[2] << 8) | sim_resp_data[3]; + fprintf(outf, "File size: %u\n", curfile_total_size); + curfile_structure = sim_resp_data[13]; + switch (curfile_structure) { + case 0x00: + fprintf(outf, "Structure: transparent\n"); + break; + case 0x01: + fprintf(outf, "Structure: linear fixed\n"); + goto ef_record_based; + case 0x03: + fprintf(outf, "Structure: cyclic\n"); + ef_record_based: + if (sim_resp_data_len < 15) { + fprintf(stderr, + "error: response length of %u bytes is too short for record-based EF\n", + sim_resp_data_len); + return(-1); + } + fprintf(outf, "Record length: %u\n", sim_resp_data[14]); + curfile_record_len = sim_resp_data[14]; + if (curfile_record_len && + curfile_total_size % curfile_record_len == 0) { + curfile_record_count = + curfile_total_size / curfile_record_len; + fprintf(outf, "Number of records: %u\n", + curfile_record_count); + } else + curfile_record_count = 0; + break; + default: + fprintf(outf, "Structure: %02X (unknown)\n", + curfile_structure); + } + fprintf(outf, "File status: %02X\n", sim_resp_data[11]); + show_access_conditions(outf, "UPDATE", sim_resp_data[8] & 0xF); + show_access_conditions(outf, "READ & SEEK", + sim_resp_data[8] >> 4); + show_access_conditions(outf, "INCREASE", sim_resp_data[9] >> 4); + show_access_conditions(outf, "INVALIDATE", + sim_resp_data[10] & 0xF); + show_access_conditions(outf, "REHABILITATE", + sim_resp_data[10] >> 4); + break; + default: + fprintf(outf, "File type: %02X (unknown)\n", sim_resp_data[6]); + } + return(0); +} + +parse_ef_select_response() +{ + if (sim_resp_data_len < 14) { + fprintf(stderr, + "error: SELECT response length of %u bytes is too short\n", + sim_resp_data_len); + return(-1); + } + if (sim_resp_data[6] != 0x04) { + fprintf(stderr, "error: selected file is not an EF\n"); + return(-1); + } + curfile_total_size = (sim_resp_data[2] << 8) | sim_resp_data[3]; + curfile_structure = sim_resp_data[13]; + switch (curfile_structure) { + case 0x00: + /* transparent */ + break; + case 0x01: + case 0x03: + /* record-based */ + if (sim_resp_data_len < 15) { + fprintf(stderr, +"error: SELECT response length of %u bytes is too short for record-based EF\n", + sim_resp_data_len); + return(-1); + } + curfile_record_len = sim_resp_data[14]; + if (!curfile_record_len) { + fprintf(stderr, + "error: SELECT response indicates record length of 0\n"); + return(-1); + } + if (curfile_total_size % curfile_record_len) { + fprintf(stderr, + "error: returned file size is not divisible by record length\n"); + return(-1); + } + curfile_record_count = curfile_total_size / curfile_record_len; + if (curfile_record_count > 255) { + fprintf(stderr, + "error: EF record count exceeds protocol limit\n"); + return(-1); + } + break; + default: + fprintf(stderr, "error: unknown EF structure code %02X\n", + curfile_structure); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/sjs1_hacks.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/sjs1_hacks.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,114 @@ +/* + * This module implements a few special commands for the recently + * discontinued sysmoUSIM-SJS1 card model from Sysmocom. These commands + * are NOT applicable to the successor sysmoISIM-SJA2 card model! + */ + +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +/* + * SJS1 is natively a UICC, supporting the classic GSM 11.11 SIM protocol + * only as a backward compatibility mode. The makers of that UICC CardOS + * clearly did not want people to do administrative programming via the + * GSM 11.11 SIM protocol, instead their vision was that admin programming + * should only be done in UICC mode. Toward this end, SJS1 cards do not + * accept VERIFY CHV commands with CLA=0xA0 P2=0x0A for ADM1 authentication, + * instead they only accept VERIFY PIN with CLA=0x00 for this purpose. + * + * They did leave one open loophole, however: if the UICC-style VERIFY PIN + * command with P2=0x0A for ADM1 authentication is given as the very first + * command in the card session, then it can be followed either by other + * UICC protocol commands (making a UICC card session), or by CLA=0xA0 + * protocol commands, making a GSM 11.11 SIM session with ADM1 authentication. + * In other words, they allow one special exception to the general rule + * where SIM and UICC protocol commands are never allowed to mix in the + * same card session. + */ + +cmd_verify_sjs1_adm1(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* UICC-style VERIFY PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x20; + cmd[2] = 0x00; + cmd[3] = 0x0A; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +/* + * Early sysmoUSIM-SJS1 cards (those sold in 2017, but not the very last + * ones sold in late 2020) were shipped with a misprogrammed MSISDN record. + * Our fix-sysmo-msisdn command fixes this particular misprogramming. + */ + +cmd_fix_sysmo_msisdn() +{ + int rc; + unsigned n; + u_char newrec[34]; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_MSISDN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_MSISDN is not linear fixed\n"); + return(-1); + } + if (curfile_record_len != 34) { + fprintf(stderr, + "error: expected EF_MSISDN record length of 34 bytes, got %u\n", + curfile_record_len); + return(-1); + } + rc = readrec_op(1, 0x04, 34); + if (rc < 0) + return(rc); + for (n = 0; n < 18; n++) { + if (sim_resp_data[n] != 0xFF) { + fprintf(stderr, + "error: non-FF data in the first 18 bytes of alpha tag area\n"); + return(-1); + } + } + if (sim_resp_data[18] == 0xFF && sim_resp_data[19] == 0xFF) { + printf( + "last 2 bytes of alpha tag area are clear - already fixed?\n"); + return(0); + } + if (sim_resp_data[18] != 0x07 || sim_resp_data[19] != 0x91) { + fprintf(stderr, + "error: bytes 18 & 19 don't match expected bogus programming\n"); + return(-1); + } + memset(newrec, 0xFF, 34); + memcpy(newrec + 20, sim_resp_data + 18, 8); + return update_rec_op(1, 0x04, newrec, 34); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/smserase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/smserase.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,121 @@ +/* + * This module implements the sms-erase family of commands. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" +#include "file_id.h" + +#define SMS_RECORD_LEN 176 + +static +select_ef_sms() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_SMS); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_SMS is not linear fixed\n"); + return(-1); + } + if (curfile_record_len != SMS_RECORD_LEN) { + fprintf(stderr, + "error: EF_SMS has record length of %u bytes, expected 176\n", + curfile_record_len); + return(-1); + } + return(0); +} + +cmd_sms_erase_all(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[SMS_RECORD_LEN]; + + rc = select_ef_sms(); + if (rc < 0) + return(rc); + memset(record, 0xFF, SMS_RECORD_LEN); + record[0] = 0; + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, record, SMS_RECORD_LEN); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_sms_erase_one(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[SMS_RECORD_LEN]; + + rc = select_ef_sms(); + if (rc < 0) + return(rc); + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, SMS_RECORD_LEN); + record[0] = 0; + return update_rec_op(recno, 0x04, record, SMS_RECORD_LEN); +} + +cmd_sms_erase_range(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + u_char record[SMS_RECORD_LEN]; + + rc = select_ef_sms(); + if (rc < 0) + return(rc); + startrec = strtoul(argv[1], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (!strcmp(argv[2], "end")) + endrec = curfile_record_count; + else { + endrec = strtoul(argv[2], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } + memset(record, 0xFF, SMS_RECORD_LEN); + record[0] = 0; + for (recno = startrec; recno <= endrec; recno++) { + rc = update_rec_op(recno, 0x04, record, SMS_RECORD_LEN); + if (rc < 0) + return(rc); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/smsp_common.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/smsp_common.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,33 @@ +/* + * This module implements some common functions for working with EF_SMSP. + */ + +#include +#include "curfile.h" +#include "file_id.h" + +select_ef_smsp() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_SMSP); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_SMSP is not linear fixed\n"); + return(-1); + } + if (curfile_record_len < 28) { + fprintf(stderr, + "error: EF_SMSP has record length of %u bytes, less than minimum 28\n", + curfile_record_len); + return(-1); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/smsp_dump.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/smsp_dump.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,130 @@ +/* + * This module implements intelligent dumping of EF_SMSP (smsp-dump). + */ + +#include +#include +#include "simresp.h" +#include "curfile.h" + +static +check_blank_area(dp, endp) + u_char *dp, *endp; +{ + while (dp < endp) + if (*dp++ != 0xFF) + return(-1); + return(0); +} + +static void +dump_da_field(binaddr, outf) + u_char *binaddr; + FILE *outf; +{ + char digits[21]; + + fputs("DA=", outf); + if (binaddr[0] < 1 || binaddr[0] > 20) { +malformed: fputs("malformed ", outf); + return; + } + if ((binaddr[0] & 1) && (binaddr[(binaddr[0] >> 1) + 2] & 0xF0) != 0xF0) + goto malformed; + if (check_blank_area(binaddr + 2 + ((binaddr[0] + 1) >> 1), + binaddr + 12) < 0) + goto malformed; + /* all checks passed */ + decode_address_digits(binaddr + 2, digits, binaddr[0]); + fprintf(outf, "%s,0x%02X ", digits, binaddr[1]); +} + +static void +dump_sca_field(binaddr, outf) + u_char *binaddr; + FILE *outf; +{ + char digits[21]; + int rc; + + fputs("SC=", outf); + if (binaddr[0] < 2 || binaddr[0] > 11) { +malformed: fputs("malformed ", outf); + return; + } + rc = decode_phone_number(binaddr + 2, binaddr[0] - 1, digits); + if (rc < 0) + goto malformed; + rc = check_blank_area(binaddr + 1 + binaddr[0], binaddr + 12); + if (rc < 0) + goto malformed; + /* all checks passed */ + fprintf(outf, "%s,0x%02X ", digits, binaddr[1]); +} + +static void +dump_record(recno, outf) + unsigned recno; + FILE *outf; +{ + int rc; + unsigned textlen; + u_char *fixp; + + fprintf(outf, "#%u: ", recno); + if (sim_resp_data_len > 28) { + rc = validate_alpha_field(sim_resp_data, + sim_resp_data_len - 28, + &textlen); + if (rc < 0) { +malformed: fprintf(outf, "malformed record\n"); + return; + } + } else + textlen = 0; + fixp = sim_resp_data + sim_resp_data_len - 28; + if ((fixp[0] & 0xE0) != 0xE0) + goto malformed; + if ((fixp[0] & 0x01) && check_blank_area(fixp + 1, fixp + 13) < 0) + goto malformed; + if ((fixp[0] & 0x02) && check_blank_area(fixp + 13, fixp + 25) < 0) + goto malformed; + if ((fixp[0] & 0x04) && fixp[25] != 0xFF) + goto malformed; + if ((fixp[0] & 0x08) && fixp[26] != 0xFF) + goto malformed; + if ((fixp[0] & 0x10) && fixp[27] != 0xFF) + goto malformed; + /* basic checks passed, emit present fields */ + if (!(fixp[0] & 0x01)) + dump_da_field(fixp + 1, outf); + if (!(fixp[0] & 0x02)) + dump_sca_field(fixp + 13, outf); + if (!(fixp[0] & 0x04)) + fprintf(outf, "PID=0x%02X ", fixp[25]); + if (!(fixp[0] & 0x08)) + fprintf(outf, "DCS=0x%02X ", fixp[26]); + if (!(fixp[0] & 0x10)) + fprintf(outf, "VP=%u ", fixp[27]); + print_alpha_field(sim_resp_data, textlen, outf); + putc('\n', outf); +} + +cmd_smsp_dump(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned recno; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + dump_record(recno, outf); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/smsp_erase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/smsp_erase.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,88 @@ +/* + * This module implements the smsp-erase family of commands. + */ + +#include +#include +#include +#include +#include +#include "curfile.h" + +cmd_smsp_erase_all(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255]; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + memset(record, 0xFF, curfile_record_len); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_smsp_erase_one(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255]; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_smsp_erase_range(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + u_char record[255]; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + startrec = strtoul(argv[1], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (!strcmp(argv[2], "end")) + endrec = curfile_record_count; + else { + endrec = strtoul(argv[2], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } + memset(record, 0xFF, curfile_record_len); + for (recno = startrec; recno <= endrec; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/smsp_restore.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/smsp_restore.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,248 @@ +/* + * This module implements the smsp-restore command. + */ + +#include +#include +#include +#include +#include +#include +#include "curfile.h" + +extern FILE *open_script_input_file(); + +extern char *alpha_from_file_qstring(); +extern char *alpha_from_file_hex(); + +static char * +parse_da(cp, bina, filename_for_errs, lineno_for_errs) + char *cp, *filename_for_errs; + u_char *bina; +{ + u_char digits[20]; + unsigned ndigits, num_digit_bytes; + int c; + + if (digit_char_to_gsm(*cp) < 0) { +inv_syntax: fprintf(stderr, "%s line %d: DA= parameter invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(0); + } + for (ndigits = 0; ; ndigits++) { + c = digit_char_to_gsm(*cp); + if (c < 0) + break; + cp++; + if (ndigits >= 20) { + fprintf(stderr, "%s line %d: too many number digits\n", + filename_for_errs, lineno_for_errs); + return(0); + } + digits[ndigits] = c; + } + bina[0] = ndigits; + if (ndigits & 1) + digits[ndigits++] = 0xF; + num_digit_bytes = ndigits >> 1; + pack_digit_bytes(digits, bina + 2, num_digit_bytes); + if (*cp++ != ',') + goto inv_syntax; + if (cp[0] != '0' || cp[1] != 'x' && cp[1] != 'X' || !isxdigit(cp[2]) || + !isxdigit(cp[3]) || !isspace(cp[4])) + goto inv_syntax; + bina[1] = strtoul(cp, 0, 16); + cp += 5; + while (isspace(*cp)) + cp++; + return(cp); +} + +static char * +parse_sc(cp, bina, filename_for_errs, lineno_for_errs) + char *cp, *filename_for_errs; + u_char *bina; +{ + u_char digits[20]; + unsigned ndigits, num_digit_bytes; + int c; + + if (digit_char_to_gsm(*cp) < 0) { +inv_syntax: fprintf(stderr, "%s line %d: SC= parameter invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(0); + } + for (ndigits = 0; ; ndigits++) { + c = digit_char_to_gsm(*cp); + if (c < 0) + break; + cp++; + if (ndigits >= 20) { + fprintf(stderr, "%s line %d: too many number digits\n", + filename_for_errs, lineno_for_errs); + return(0); + } + digits[ndigits] = c; + } + if (ndigits & 1) + digits[ndigits++] = 0xF; + num_digit_bytes = ndigits >> 1; + bina[0] = num_digit_bytes + 1; + pack_digit_bytes(digits, bina + 2, num_digit_bytes); + if (*cp++ != ',') + goto inv_syntax; + if (cp[0] != '0' || cp[1] != 'x' && cp[1] != 'X' || !isxdigit(cp[2]) || + !isxdigit(cp[3]) || !isspace(cp[4])) + goto inv_syntax; + bina[1] = strtoul(cp, 0, 16); + cp += 5; + while (isspace(*cp)) + cp++; + return(cp); +} + +static +process_record(line, filename_for_errs, lineno_for_errs) + char *line, *filename_for_errs; +{ + unsigned recno; + u_char record[255], *fixp; + char *cp; + + recno = strtoul(line+1, 0, 10); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "%s line %d: record number is out of range\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + cp = line + 1; + while (isdigit(*cp)) + cp++; + if (*cp++ != ':') { +inv_syntax: fprintf(stderr, "%s line %d: invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + while (isspace(*cp)) + cp++; + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 28; + if (!strncasecmp(cp, "DA=", 3)) { + cp += 3; + cp = parse_da(cp, fixp + 1, filename_for_errs, lineno_for_errs); + if (!cp) + return(-1); + fixp[0] &= 0xFE; + } + if (!strncasecmp(cp, "SC=", 3)) { + cp += 3; + cp = parse_sc(cp, fixp+13, filename_for_errs, lineno_for_errs); + if (!cp) + return(-1); + fixp[0] &= 0xFD; + } + if (!strncasecmp(cp, "PID=", 4)) { + cp += 4; + if (!isdigit(*cp)) { + fprintf(stderr, + "%s line %d: PID= parameter invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + fixp[25] = strtoul(cp, 0, 0); + fixp[0] &= 0xFB; + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (!strncasecmp(cp, "DCS=", 4)) { + cp += 4; + if (!isdigit(*cp)) { + fprintf(stderr, + "%s line %d: DCS= parameter invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + fixp[26] = strtoul(cp, 0, 0); + fixp[0] &= 0xF7; + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (!strncasecmp(cp, "VP=", 3)) { + cp += 3; + if (!isdigit(*cp)) { + fprintf(stderr, + "%s line %d: VP= parameter invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + fixp[27] = strtoul(cp, 0, 0); + fixp[0] &= 0xEF; + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (*cp == '"') { + cp++; + cp = alpha_from_file_qstring(cp, record, + curfile_record_len - 28, + filename_for_errs, + lineno_for_errs); + if (!cp) + return(-1); + } else if (!strncasecmp(cp, "HEX", 3)) { + cp += 3; + while (isspace(*cp)) + cp++; + cp = alpha_from_file_hex(cp, record, curfile_record_len - 28, + filename_for_errs, lineno_for_errs); + if (!cp) + return(-1); + } else + goto inv_syntax; + while (isspace(*cp)) + cp++; + if (*cp) + goto inv_syntax; + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_smsp_restore(argc, argv) + char **argv; +{ + int rc; + FILE *inf; + int lineno; + char linebuf[1024]; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + inf = open_script_input_file(argv[1]); + if (!inf) { + perror(argv[1]); + return(-1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { + if (!index(linebuf, '\n')) { + fprintf(stderr, + "%s line %d: too long or missing newline\n", + argv[1], lineno); + fclose(inf); + return(-1); + } + if (linebuf[0] != '#' || !isdigit(linebuf[1])) + continue; + rc = process_record(linebuf, argv[1], lineno); + if (rc < 0) { + fclose(inf); + return(rc); + } + } + fclose(inf); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/smsp_set.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/smsp_set.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,167 @@ +/* + * This module implements the user-oriented smsp-set and smsp-set-tag commands. + */ + +#include +#include +#include +#include +#include +#include +#include "curfile.h" + +static +set_param_da(arg, fixp) + char *arg; + u_char *fixp; +{ + int rc; + + rc = encode_phone_number_arg(arg, fixp + 1, 1); + if (rc < 0) + return(rc); + fixp[0] &= 0xFE; + return(0); +} + +static +set_param_sc(arg, fixp) + char *arg; + u_char *fixp; +{ + int rc; + + rc = encode_phone_number_arg(arg, fixp + 13, 0); + if (rc < 0) + return(rc); + fixp[0] &= 0xFD; + return(0); +} + +static +set_param_pid(arg, fixp) + char *arg; + u_char *fixp; +{ + char *endp; + + if (!isdigit(*arg)) { +inv: fprintf(stderr, "error: invalid PID= parameter\n"); + return(-1); + } + fixp[25] = strtoul(arg, &endp, 0); + if (*endp) + goto inv; + fixp[0] &= 0xFB; + return(0); +} + +static +set_param_dcs(arg, fixp) + char *arg; + u_char *fixp; +{ + char *endp; + + if (!isdigit(*arg)) { +inv: fprintf(stderr, "error: invalid DCS= parameter\n"); + return(-1); + } + fixp[26] = strtoul(arg, &endp, 0); + if (*endp) + goto inv; + fixp[0] &= 0xF7; + return(0); +} + +static +set_param_vp(arg, fixp) + char *arg; + u_char *fixp; +{ + char *endp; + + if (!isdigit(*arg)) { +inv: fprintf(stderr, "error: invalid VP= parameter\n"); + return(-1); + } + fixp[27] = strtoul(arg, &endp, 0); + if (*endp) + goto inv; + fixp[0] &= 0xEF; + return(0); +} + +static +set_param(arg, fixp) + char *arg; + u_char *fixp; +{ + if (!strncasecmp(arg, "DA=", 3)) + return set_param_da(arg + 3, fixp); + if (!strncasecmp(arg, "SC=", 3)) + return set_param_sc(arg + 3, fixp); + if (!strncasecmp(arg, "PID=", 4)) + return set_param_pid(arg + 4, fixp); + if (!strncasecmp(arg, "DCS=", 4)) + return set_param_dcs(arg + 4, fixp); + if (!strncasecmp(arg, "VP=", 3)) + return set_param_vp(arg + 3, fixp); + fprintf(stderr, "error: non-understood parameter \"%s\"\n", arg); + return(-1); +} + +cmd_smsp_set(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + char **ap; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 28; + for (ap = argv + 2; *ap; ap++) { + rc = set_param(*ap, fixp); + if (rc < 0) + return(rc); + } + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_smsp_set_tag(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + char **ap; + + rc = select_ef_smsp(); + if (rc < 0) + return(rc); + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + rc = qstring_arg_to_gsm7(argv[2], record, curfile_record_len - 28); + if (rc < 0) + return(rc); + fixp = record + curfile_record_len - 28; + for (ap = argv + 3; *ap; ap++) { + rc = set_param(*ap, fixp); + if (rc < 0) + return(rc); + } + return update_rec_op(recno, 0x04, record, curfile_record_len); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/sstlist.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/sstlist.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,70 @@ +/* + * This module implements the sst command, listing the SIM Service Table + * in a human-readable, yet very compact form: just a list of activated + * (or allocated but not activated, specially marked) service numbers. + */ + +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +cmd_sst(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned byte, pos, code, nserv, linelen; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_SST); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00) { + fprintf(stderr, "error: EF_SST is not transparent\n"); + return(-1); + } + if (curfile_total_size < 2) { + fprintf(stderr, + "error: EF_SST is shorter than spec minimum of 2 bytes\n"); + return(-1); + } + if (curfile_total_size > 256) { + fprintf(stderr, + "error: EF_SST is longer than our 256 byte limit\n"); + return(-1); + } + rc = readbin_op(0, curfile_total_size); + if (rc < 0) + return(rc); + linelen = 0; + for (byte = 0, nserv = 1; byte < curfile_total_size; byte++) { + for (pos = 0; pos < 8; pos += 2, nserv++) { + code = (sim_resp_data[byte] >> pos) & 3; + if (!(code & 1)) + continue; + if (linelen > 73) { + putc('\n', outf); + linelen = 0; + } + if (linelen) { + putc(' ', outf); + linelen++; + } + linelen += fprintf(outf, "%u", nserv); + if (!(code & 2)) { + putc('^', outf); + linelen++; + } + } + } + if (linelen) + putc('\n', outf); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/sstprog.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/sstprog.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,98 @@ +/* + * This module implements the write-sst command for admin-level + * programming of SIM cards. + */ + +#include +#include +#include +#include +#include +#include +#include "curfile.h" +#include "file_id.h" + +extern FILE *open_script_input_file(); + +cmd_write_sst(argc, argv) + char **argv; +{ + u_char sst[255]; + FILE *inf; + int lineno, rc; + char linebuf[1024], *cp, *np; + unsigned num, code, max_serv; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_SST); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00) { + fprintf(stderr, "error: EF_SST is not transparent\n"); + return(-1); + } + if (curfile_total_size < 2) { + fprintf(stderr, + "error: EF_SST is shorter than spec minimum of 2 bytes\n"); + return(-1); + } + if (curfile_total_size > 255) { + fprintf(stderr, + "error: EF_SST is longer than our 255 byte limit\n"); + return(-1); + } + memset(sst, 0, curfile_total_size); + max_serv = curfile_total_size * 4; + inf = open_script_input_file(argv[1]); + if (!inf) { + perror(argv[1]); + return(-1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { + if (!index(linebuf, '\n')) { + fprintf(stderr, + "%s line %d: too long or missing newline\n", + argv[1], lineno); + fclose(inf); + return(-1); + } + for (cp = linebuf; ; ) { + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + break; + if (!isdigit(*cp)) { +inv_syntax: fprintf(stderr, "%s line %d: invalid syntax\n", + argv[1], lineno); + fclose(inf); + return(-1); + } + num = strtoul(cp, 0, 10); + while (isdigit(*cp)) + cp++; + if (*cp == '^') { + cp++; + code = 1; + } else + code = 3; + if (*cp && !isspace(*cp)) + goto inv_syntax; + if (num < 1 || num > max_serv) { + fprintf(stderr, + "%s line %d: service number is out of range\n", + argv[1], lineno); + fclose(inf); + return(-1); + } + num--; + sst[num >> 2] |= code << ((num & 3) << 1); + } + } + fclose(inf); + return update_bin_op(0, sst, curfile_total_size); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/stktest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/stktest.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,81 @@ +/* + * This module implements some commands for testing SIM Toolkit functionality, + * just enough to be able to simulate SMS-PP SIM data download operations. + */ + +#include +#include +#include +#include "simresp.h" + +cmd_terminal_profile(argc, argv) + char **argv; +{ + u_char cmd[260]; + int rc; + unsigned len; + + rc = decode_hex_data_from_string(argv[1], cmd + 5, 1, 255); + if (rc < 0) + return(rc); + len = rc; + /* TERMINAL PROFILE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x10; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = len; + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + printf("%04X\n", sim_resp_sw); + return(0); +} + +cmd_envelope(argc, argv) + char **argv; +{ + u_char cmd[260]; + int rc; + unsigned len; + + rc = read_hex_data_file(argv[1], cmd + 5, 255); + if (rc < 0) + return(rc); + len = rc; + /* ENVELOPE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xC2; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = len; + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + printf("%04X\n", sim_resp_sw); + return(0); +} + +cmd_envelope_imm(argc, argv) + char **argv; +{ + u_char cmd[260]; + int rc; + unsigned len; + + rc = decode_hex_data_from_string(argv[1], cmd + 5, 1, 255); + if (rc < 0) + return(rc); + len = rc; + /* ENVELOPE command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xC2; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = len; + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + printf("%04X\n", sim_resp_sw); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/telsum.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/telsum.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,83 @@ +/* + * This module implements the telecom-sum (summary info) command. + */ + +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +do_phonebook_file(file_id, book_name) + unsigned file_id; + char *book_name; +{ + int rc; + + rc = select_op(file_id); + if (rc < 0) { + printf("%s not present\n", book_name); + return(rc); + } + rc = parse_ef_select_response(); + if (rc < 0) { + fprintf(stderr, "error occurred on SELECT of EF_%s\n", + book_name); + return(rc); + } + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_%s is not linear fixed\n", + book_name); + return(-1); + } + if (curfile_record_len < 14) { + fprintf(stderr, + "error: EF_%s has record length of %u bytes, less than minimum 14\n", + book_name, curfile_record_len); + return(-1); + } + printf("%s has %u entries, %u bytes of alpha tag\n", book_name, + curfile_record_count, curfile_record_len - 14); + return(0); +} + +static +do_sms_store() +{ + int rc; + + rc = select_op(EF_SMS); + if (rc < 0) { + printf("EF_SMS not present\n"); + return(rc); + } + rc = parse_ef_select_response(); + if (rc < 0) { + fprintf(stderr, "error occurred on SELECT of EF_SMS\n"); + return(rc); + } + if (curfile_structure != 0x01 || curfile_record_len != 176) { + fprintf(stderr, + "error: EF_SMS is not linear fixed with 176-byte records\n"); + return(-1); + } + printf("SMS store has %u entries\n", curfile_record_count); + return(0); +} + +cmd_telecom_sum() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + do_phonebook_file(EF_ADN, "ADN"); + do_phonebook_file(EF_FDN, "FDN"); + do_phonebook_file(EF_SDN, "SDN"); + do_phonebook_file(EF_MSISDN, "MSISDN"); + do_sms_store(); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/usersum.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/usersum.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,189 @@ +/* + * This module implements the user-sum (summary info) command. + */ + +#include +#include +#include +#include +#include +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +#define SST_BYTES_USED 15 + +static +read_sst(sstbuf) + u_char *sstbuf; +{ + int rc; + unsigned rdlen; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_SST); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00) { + fprintf(stderr, "error: EF_SST is not transparent\n"); + return(-1); + } + if (curfile_total_size < 2) { + fprintf(stderr, + "error: EF_SST is shorter than spec minimum of 2 bytes\n"); + return(-1); + } + rdlen = curfile_total_size; + if (rdlen > SST_BYTES_USED) + rdlen = SST_BYTES_USED; + rc = readbin_op(0, rdlen); + if (rc < 0) + return(rc); + bcopy(sim_resp_data, sstbuf, rdlen); + if (rdlen < SST_BYTES_USED) + bzero(sstbuf + rdlen, SST_BYTES_USED - rdlen); + return(0); +} + +static +do_phonebook_file(file_id, ef_name, book_name, outf) + unsigned file_id; + char *ef_name, *book_name; + FILE *outf; +{ + int rc; + + rc = select_op(file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01 && curfile_structure != 0x03) { + fprintf(stderr, "error: %s is not record-structured\n", + ef_name); + return(-1); + } + if (curfile_record_len < 14) { + fprintf(stderr, + "error: %s has record length of %u bytes, less than minimum 14\n", + ef_name, curfile_record_len); + return(-1); + } + fprintf(outf, "%s: %u entries, %u bytes of alpha tag\n", book_name, + curfile_record_count, curfile_record_len - 14); + return(0); +} + +static +do_sms_store(outf) + FILE *outf; +{ + int rc; + + rc = select_op(EF_SMS); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01 || curfile_record_len != 176) { + fprintf(stderr, + "error: EF_SMS is not linear fixed with 176-byte records\n"); + return(-1); + } + fprintf(outf, "SMS store: %u entries\n", curfile_record_count); + return(0); +} + +static +do_smsp_store(outf) + FILE *outf; +{ + int rc; + + rc = select_op(EF_SMSP); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_SMSP is not linear fixed\n"); + return(-1); + } + if (curfile_record_len < 28) { + fprintf(stderr, + "error: EF_SMSP has record length of %u bytes, less than minimum 14\n", + curfile_record_len); + return(-1); + } + fprintf(outf, + "SMS parameter store: %u entries, %u bytes of alpha tag\n", + curfile_record_count, curfile_record_len - 28); + return(0); +} + +cmd_user_sum(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + u_char sst[SST_BYTES_USED]; + + rc = read_sst(sst); + if (rc < 0) + return(rc); + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + if ((sst[0] & 0x0C) == 0x0C) { + rc = do_phonebook_file(EF_ADN, "EF_ADN", "ADN phonebook", outf); + if (rc < 0) + return(rc); + } + if ((sst[0] & 0x30) == 0x30) { + rc = do_phonebook_file(EF_FDN, "EF_FDN", "FDN phonebook", outf); + if (rc < 0) + return(rc); + } + if ((sst[0] & 0xC0) == 0xC0) { + rc = do_sms_store(outf); + if (rc < 0) + return(rc); + } + if ((sst[1] & 0x03) == 0x03) + fprintf(outf, "AoC service present\n"); + if ((sst[2] & 0x03) == 0x03) { + rc = do_phonebook_file(EF_MSISDN, "EF_MSISDN", "MSISDN record", + outf); + if (rc < 0) + return(rc); + } + if ((sst[2] & 0xC0) == 0xC0) { + rc = do_smsp_store(outf); + if (rc < 0) + return(rc); + } + if ((sst[3] & 0x03) == 0x03) { + rc = do_phonebook_file(EF_LND, "EF_LND", "LND cyclic store", + outf); + if (rc < 0) + return(rc); + } + if ((sst[4] & 0x0C) == 0x0C) { + rc = do_phonebook_file(EF_SDN, "EF_SDN", "SDN phonebook", outf); + if (rc < 0) + return(rc); + } + if ((sst[13] & 0x03) == 0x03) + fprintf(outf, "MBDN present\n"); + if ((sst[13] & 0x0C) == 0x0C) + fprintf(outf, "MWIS present\n"); + return(0); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/writecmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/writecmd.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include "curfile.h" + +cmd_update_bin(argc, argv) + char **argv; +{ + unsigned offset, len; + u_char data[255]; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0xFFFF) { + fprintf(stderr, "error: offset argument is out of range\n"); + return(-1); + } + rc = read_hex_data_file(argv[2], data, 255); + if (rc < 0) + return(rc); + len = rc; + return update_bin_op(offset, data, len); +} + +cmd_update_bin_imm(argc, argv) + char **argv; +{ + unsigned offset, len; + u_char data[255]; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0xFFFF) { + fprintf(stderr, "error: offset argument is out of range\n"); + return(-1); + } + rc = decode_hex_data_from_string(argv[2], data, 1, 255); + if (rc < 0) + return(rc); + len = rc; + return update_bin_op(offset, data, len); +} + +cmd_update_rec(argc, argv) + char **argv; +{ + unsigned recno, mode; + u_char data[255]; + int rc; + + if (!strcmp(argv[1], "prev")) { + recno = 0; + mode = 0x03; + } else { + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > 255) { + fprintf(stderr, + "error: record number argument is out of range\n"); + return(-1); + } + mode = 0x04; + } + rc = read_hex_data_file(argv[2], data, 255); + if (rc < 0) + return(rc); + if (rc != curfile_record_len) { + fprintf(stderr, "error: hex data length != EF record length\n"); + return(-1); + } + return update_rec_op(recno, mode, data, curfile_record_len); +} + +cmd_update_rec_imm(argc, argv) + char **argv; +{ + unsigned recno, mode; + u_char data[255]; + int rc; + + if (!strcmp(argv[1], "prev")) { + recno = 0; + mode = 0x03; + } else { + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > 255) { + fprintf(stderr, + "error: record number argument is out of range\n"); + return(-1); + } + mode = 0x04; + } + rc = decode_hex_data_from_string(argv[2], data, 1, 255); + if (rc < 0) + return(rc); + if (rc != curfile_record_len) { + fprintf(stderr, "error: hex data length != EF record length\n"); + return(-1); + } + return update_rec_op(recno, mode, data, curfile_record_len); +} + +cmd_update_rec_fill(argc, argv) + char **argv; +{ + unsigned recno, mode, fill_byte; + u_char data[255]; + + if (!strcmp(argv[1], "prev")) { + recno = 0; + mode = 0x03; + } else { + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > 255) { + fprintf(stderr, + "error: record number argument is out of range\n"); + return(-1); + } + mode = 0x04; + } + fill_byte = strtoul(argv[2], 0, 16); + if (fill_byte > 0xFF) { + fprintf(stderr, "error: invalid fill byte argument\n"); + return(-1); + } + memset(data, fill_byte, curfile_record_len); + return update_rec_op(recno, mode, data, curfile_record_len); +} diff -r c9ef9e91dd8e -r ddd767f6e15b simtool/writeops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/writeops.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include "simresp.h" + +update_bin_op(offset, data, len) + unsigned offset, len; + u_char *data; +{ + u_char cmd[260]; + int rc; + + /* UPDATE BINARY command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD6; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = len; + bcopy(data, cmd + 5, len); + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to UPDATE BINARY: %04X\n", + sim_resp_sw); + return(-1); + } + return(0); +} + +update_rec_op(recno, mode, data, len) + unsigned recno, mode, len; + u_char *data; +{ + u_char cmd[260]; + int rc; + + /* UPDATE RECORD command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xDC; + cmd[2] = recno; + cmd[3] = mode; + cmd[4] = len; + bcopy(data, cmd + 5, len); + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to UPDATE RECORD: %04X\n", + sim_resp_sw); + return(-1); + } + return(0); +}