changeset 146:ce2a880ab704

fc-uicc-tool: pb-update commands ported over
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 06 Feb 2021 02:45:29 +0000
parents 14dee03e9675
children 43463dc91431
files uicc/Makefile uicc/dispatch.c uicc/pbupdate.c
diffstat 3 files changed, 515 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- 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}
--- 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},
--- /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 <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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);
+}