# HG changeset patch # User Mychaela Falconia # Date 1612579529 0 # Node ID ce2a880ab704ac4097b94393e87583bff26544d0 # Parent 14dee03e9675312be61d4c0678a7de49b37da531 fc-uicc-tool: pb-update commands ported over diff -r 14dee03e9675 -r ce2a880ab704 uicc/Makefile --- a/uicc/Makefile Sat Feb 06 02:17:51 2021 +0000 +++ b/uicc/Makefile Sat Feb 06 02:45:29 2021 +0000 @@ -3,8 +3,8 @@ PROG= fc-uicc-tool OBJS= alpha_decode.o alpha_valid.o apdu.o atr.o cardconnect.o dispatch.o \ dumpdir.o exit.o globals.o hexdump.o hexread.o hexstr.o hlread.o main.o\ - names.o pbcommon.o pbdump.o readcmd.o readops.o script.o select.o \ - telsum.o writecmd.o writeops.o + names.o pbcommon.o pbdump.o pbupdate.o readcmd.o readops.o script.o \ + select.o telsum.o writecmd.o writeops.o INSTBIN=/opt/freecalypso/bin all: ${PROG} diff -r 14dee03e9675 -r ce2a880ab704 uicc/dispatch.c --- a/uicc/dispatch.c Sat Feb 06 02:17:51 2021 +0000 +++ b/uicc/dispatch.c Sat Feb 06 02:45:29 2021 +0000 @@ -13,6 +13,9 @@ extern int cmd_iccid(); extern int cmd_pb_dump(); extern int cmd_pb_dump_rec(); +extern int cmd_pb_update(); +extern int cmd_pb_update_imm(); +extern int cmd_pb_update_imm_hex(); extern int cmd_readbin(); extern int cmd_readrec(); extern int cmd_select(); @@ -39,6 +42,9 @@ {"iccid", 0, 0, cmd_iccid}, {"pb-dump", 1, 2, cmd_pb_dump}, {"pb-dump-rec", 2, 3, cmd_pb_dump_rec}, + {"pb-update", 2, 2, cmd_pb_update}, + {"pb-update-imm", 3, 4, cmd_pb_update_imm}, + {"pb-update-imm-hex", 4, 4, cmd_pb_update_imm_hex}, {"quit", 0, 0, good_exit}, {"readbin", 2, 2, cmd_readbin}, {"readrec", 1, 2, cmd_readrec}, diff -r 14dee03e9675 -r ce2a880ab704 uicc/pbupdate.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/pbupdate.c Sat Feb 06 02:45:29 2021 +0000 @@ -0,0 +1,507 @@ +/* + * This module implements the pb-update command. + */ + +#include +#include +#include +#include +#include +#include + +static u_char gsm7_encode_table[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x00 */ + 0xFF, 0xFF, '\n', 0xFF, 0xFF, '\r', 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x10 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + ' ', '!', '"', '#', 0x02, '%', '&', 0x27, /* 0x20 */ + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', /* 0x30 */ + '8', '9', ':', ';', '<', '=', '>', '?', + 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40 */ + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50 */ + 'X', 'Y', 'Z', 0xBC, 0xAF, 0xBE, 0x94, 0x11, + 0xFF, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60 */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70 */ + 'x', 'y', 'z', 0xA8, 0xC0, 0xA9, 0xBD, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x80 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x90 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x40, 0xFF, 0x01, 0x24, 0x03, 0xFF, 0x5F, /* 0xA0 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0xB0 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, + 0xFF, 0xFF, 0xFF, 0xFF, 0x5B, 0x0E, 0x1C, 0x09, /* 0xC0 */ + 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0xFF, /* 0xD0 */ + 0x0B, 0xFF, 0xFF, 0xFF, 0x5E, 0xFF, 0xFF, 0x1E, + 0x7F, 0xFF, 0xFF, 0xFF, 0x7B, 0x0F, 0x1D, 0xFF, /* 0xE0 */ + 0x04, 0x05, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7D, 0x08, 0xFF, 0xFF, 0xFF, 0x7C, 0xFF, /* 0xF0 */ + 0x0C, 0x06, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF +}; + +static +digit_char_to_gsm(ch) +{ + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return (ch - '0'); + case '*': + return 0xA; + case '#': + return 0xB; + case 'a': + case 'b': + case 'c': + return (ch - 'a' + 0xC); + case 'A': + case 'B': + case 'C': + return (ch - 'A' + 0xC); + } + return (-1); +} + +static void +pack_digit_bytes(digits, dest, num_digit_bytes) + u_char *digits, *dest; + unsigned num_digit_bytes; +{ + u_char *sp, *dp; + unsigned n; + + sp = digits; + dp = dest; + for (n = 0; n < num_digit_bytes; n++) { + *dp++ = sp[0] | (sp[1] << 4); + sp += 2; + } +} + +static char * +decode_qstring_alpha(cp, record, maxlen, filename_for_errs, lineno_for_errs) + char *cp, *filename_for_errs; + u_char *record; + unsigned maxlen; +{ + unsigned acclen, nadd; + int c; + + for (acclen = 0; ; ) { + if (*cp == '\0') { +unterm_qstring: fprintf(stderr, + "%s line %d: unterminated quoted string\n", + filename_for_errs, lineno_for_errs); + return(0); + } + if (*cp == '"') + break; + c = *cp++; + if (c == '\\') { + if (*cp == '\0') + goto unterm_qstring; + c = *cp++; + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case '"': + case '\\': + break; + default: + fprintf(stderr, + "%s line %d: non-understood backslash escape\n", + filename_for_errs, lineno_for_errs); + return(0); + } + } + c = gsm7_encode_table[c]; + if (c == 0xFF) { + fprintf(stderr, + "%s line %d: character in quoted string cannot be encoded in GSM7\n", + filename_for_errs, lineno_for_errs); + return(0); + } + if (c & 0x80) + nadd = 2; + else + nadd = 1; + if (acclen + nadd > maxlen) { + fprintf(stderr, + "%s line %d: alpha tag string is longer than SIM limit\n", + filename_for_errs, lineno_for_errs); + return(0); + } + if (c & 0x80) + record[acclen++] = 0x1B; + record[acclen++] = c & 0x7F; + } + return(cp + 1); +} + +static char * +decode_hex_alpha(cp, record, maxlen, filename_for_errs, lineno_for_errs) + char *cp, *filename_for_errs; + u_char *record; + unsigned maxlen; +{ + unsigned acclen; + + for (acclen = 0; ; ) { + if (!isxdigit(cp[0]) || !isxdigit(cp[1])) + break; + if (acclen >= maxlen) { + fprintf(stderr, + "%s line %d: alpha tag string is longer than SIM limit\n", + filename_for_errs, lineno_for_errs); + return(0); + } + record[acclen++] = (decode_hex_digit(cp[0]) << 4) | + decode_hex_digit(cp[1]); + cp += 2; + } + return(cp); +} + +static +process_record(line, pb_record_len, pb_record_count, filename_for_errs, + lineno_for_errs) + char *line, *filename_for_errs; + unsigned pb_record_len, pb_record_count; +{ + 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 > pb_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, pb_record_len); + fixp = record + pb_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 = decode_qstring_alpha(cp, record, pb_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 = decode_hex_alpha(cp, record, pb_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; + return update_rec_op(recno, 0x04, record, pb_record_len); +} + +cmd_pb_update(argc, argv) + char **argv; +{ + int rc; + FILE *inf; + int lineno; + char linebuf[1024]; + unsigned pb_record_len, pb_record_count; + + rc = phonebook_op_common(argv[1], &pb_record_len, &pb_record_count); + if (rc < 0) + return(rc); + inf = fopen(argv[2], "r"); + 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, pb_record_len, pb_record_count, + argv[2], lineno); + if (rc < 0) { + fclose(inf); + return(rc); + } + } + fclose(inf); + return(0); +} + +static +decode_number_arg(arg, fixp) + char *arg; + u_char *fixp; +{ + u_char digits[20]; + unsigned ndigits, num_digit_bytes; + char *cp, *endp; + int c; + + cp = arg; + if (*cp == '+') { + fixp[1] = 0x91; + cp++; + } else + fixp[1] = 0x81; + if (digit_char_to_gsm(*cp) < 0) { +inv_arg: fprintf(stderr, "error: invalid phone number argument\n"); + return(-1); + } + for (ndigits = 0; ; ndigits++) { + c = digit_char_to_gsm(*cp); + if (c < 0) + break; + cp++; + if (ndigits >= 20) { + fprintf(stderr, "error: too many number digits\n"); + 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 == ',') { + cp++; + if (!isdigit(*cp)) + goto inv_arg; + fixp[1] = strtoul(cp, &endp, 0); + if (*endp) + goto inv_arg; + } else if (*cp) + goto inv_arg; + return(0); +} + +static +decode_alphatag_arg(arg, record, maxlen) + char *arg; + u_char *record; + unsigned maxlen; +{ + unsigned acclen, nadd; + char *cp; + int c; + + cp = arg; + for (acclen = 0; *cp; ) { + c = *cp++; + if (c == '\\') { + if (*cp == '\0') { + fprintf(stderr, + "error: dangling backslash escape\n"); + return(-1); + } + c = *cp++; + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case '"': + case '\\': + break; + default: + fprintf(stderr, + "error: non-understood backslash escape\n"); + return(-1); + } + } + c = gsm7_encode_table[c]; + if (c == 0xFF) { + fprintf(stderr, + "error: character in alpha tag string cannot be encoded in GSM7\n"); + return(-1); + } + if (c & 0x80) + nadd = 2; + else + nadd = 1; + if (acclen + nadd > maxlen) { + fprintf(stderr, + "error: alpha tag string is longer than SIM limit\n"); + return(-1); + } + if (c & 0x80) + record[acclen++] = 0x1B; + record[acclen++] = c & 0x7F; + } + return(0); +} + +cmd_pb_update_imm(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + unsigned pb_record_len, pb_record_count; + + rc = phonebook_op_common(argv[1], &pb_record_len, &pb_record_count); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > pb_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, pb_record_len); + fixp = record + pb_record_len - 14; + rc = decode_number_arg(argv[3], fixp); + if (rc < 0) + return(rc); + if (argv[4]) { + rc = decode_alphatag_arg(argv[4], record, pb_record_len - 14); + if (rc < 0) + return(rc); + } + return update_rec_op(recno, 0x04, record, pb_record_len); +} + +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; + unsigned pb_record_len, pb_record_count; + + rc = phonebook_op_common(argv[1], &pb_record_len, &pb_record_count); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > pb_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, pb_record_len); + fixp = record + pb_record_len - 14; + rc = decode_number_arg(argv[3], fixp); + if (rc < 0) + return(rc); + rc = decode_alphatag_arg_hex(argv[4], record, pb_record_len - 14); + if (rc < 0) + return(rc); + return update_rec_op(recno, 0x04, record, pb_record_len); +}