changeset 136:a21d348e01db

fc-uicc-tool: pb-dump command ported over
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 04 Feb 2021 03:40:55 +0000
parents 51d6aaa43a7b
children c331560c15a4
files uicc/Makefile uicc/alpha_decode.c uicc/alpha_valid.c uicc/dispatch.c uicc/pbcommon.c uicc/pbdump.c
diffstat 6 files changed, 538 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/uicc/Makefile	Thu Feb 04 02:55:31 2021 +0000
+++ b/uicc/Makefile	Thu Feb 04 03:40:55 2021 +0000
@@ -1,8 +1,9 @@
 CC=	gcc
 CFLAGS=	-O2 -I/usr/include/PCSC
 PROG=	fc-uicc-tool
-OBJS=	apdu.o atr.o cardconnect.o dispatch.o exit.o globals.o hexdump.o \
-	hlread.o main.o names.o readcmd.o readops.o script.o select.o telsum.o
+OBJS=	alpha_decode.o alpha_valid.o apdu.o atr.o cardconnect.o dispatch.o \
+	exit.o globals.o hexdump.o hlread.o main.o names.o pbcommon.o pbdump.o \
+	readcmd.o readops.o script.o select.o telsum.o
 
 all:	${PROG}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uicc/alpha_decode.c	Thu Feb 04 03:40:55 2021 +0000
@@ -0,0 +1,132 @@
+/*
+ * This module contains functions for decoding and displaying alpha fields
+ * that exist in various SIM files.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static char gsm7_decode_table[128] = {
+	'@', 0,   '$',  0,   0,   0,    0,   0,
+	0,   0,   '\n', 0,   0,   '\r', 0,   0,
+	0,   '_', 0,    0,   0,   0,    0,   0,
+	0,   0,   0,    0,   0,   0,    0,   0,
+	' ', '!', '"',  '#', 0,   '%',  '&', 0x27,
+	'(', ')', '*',  '+', ',', '-',  '.', '/',
+	'0', '1', '2',  '3', '4', '5',  '6', '7',
+	'8', '9', ':',  ';', '<', '=',  '>', '?',
+	0,   'A', 'B',  'C', 'D', 'E',  'F', 'G',
+	'H', 'I', 'J',  'K', 'L', 'M',  'N', 'O',
+	'P', 'Q', 'R',  'S', 'T', 'U',  'V', 'W',
+	'X', 'Y', 'Z',  0,   0,   0,    0,   0,
+	0,   'a', 'b',  'c', 'd', 'e',  'f', 'g',
+	'h', 'i', 'j',  'k', 'l', 'm',  'n', 'o',
+	'p', 'q', 'r',  's', 't', 'u',  'v', 'w',
+	'x', 'y', 'z',  0,   0,   0,    0,   0
+};
+
+static char gsm7ext_decode_table[128] = {
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, '^', 0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, '{', '}', 0, 0, 0,   0,   0,   '\\',
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, '[', '~', ']', 0,
+	'|', 0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0
+};
+
+static
+is_gsm7_string_ok(data, nbytes)
+	u_char *data;
+	unsigned nbytes;
+{
+	u_char *dp, *endp;
+	int c;
+
+	dp = data;
+	endp = data + nbytes;
+	while (dp < endp) {
+		c = *dp++;
+		if (c == 0x1B && dp < endp)
+			c = gsm7ext_decode_table[*dp++];
+		else
+			c = gsm7_decode_table[c];
+		if (!c)
+			return(0);
+	}
+	return(1);
+}
+
+static void
+print_alpha_field_gsmdecode(data, nbytes, outf)
+	u_char *data;
+	unsigned nbytes;
+	FILE *outf;
+{
+	u_char *dp, *endp;
+	int c;
+
+	dp = data;
+	endp = data + nbytes;
+	putc('"', outf);
+	while (dp < endp) {
+		c = *dp++;
+		if (c == 0x1B && dp < endp)
+			c = gsm7ext_decode_table[*dp++];
+		else
+			c = gsm7_decode_table[c];
+		if (c == '\n') {
+			putc('\\', outf);
+			putc('n', outf);
+			continue;
+		}
+		if (c == '\r') {
+			putc('\\', outf);
+			putc('r', outf);
+			continue;
+		}
+		if (c == '"' || c == '\\')
+			putc('\\', outf);
+		putc(c, outf);
+	}
+	putc('"', outf);
+}
+
+static void
+print_alpha_field_hex(data, nbytes, outf)
+	u_char *data;
+	unsigned nbytes;
+	FILE *outf;
+{
+	u_char *dp, *endp;
+
+	fputs("HEX ", outf);
+	dp = data;
+	endp = data + nbytes;
+	while (dp < endp)
+		fprintf(outf, "%02X", *dp++);
+}
+
+void
+print_alpha_field(data, nbytes, outf)
+	u_char *data;
+	unsigned nbytes;
+	FILE *outf;
+{
+	if (!nbytes) {
+		fputs("\"\"", outf);
+		return;
+	}
+	if (data[0] & 0x80) {
+		print_alpha_field_hex(data, nbytes, outf);
+		return;
+	}
+	if (is_gsm7_string_ok(data, nbytes))
+		print_alpha_field_gsmdecode(data, nbytes, outf);
+	else
+		print_alpha_field_hex(data, nbytes, outf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uicc/alpha_valid.c	Thu Feb 04 03:40:55 2021 +0000
@@ -0,0 +1,152 @@
+/*
+ * This module contains functions for validating alpha fields
+ * that exist in various SIM files.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static
+validate_classic_gsm(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp;
+	unsigned n;
+
+	dp = data;
+	for (n = 0; n < nbytes; n++) {
+		if (*dp == 0xFF)
+			break;
+		if (*dp & 0x80)
+			return(-1);
+		dp++;
+	}
+	if (textlenp)
+		*textlenp = n;
+	for (; n < nbytes; n++)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_ucs2_80(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp, *endp;
+
+	if (nbytes < 3)
+		return(-1);
+	dp = data + 1;
+	endp = data + nbytes;
+	while (dp < endp) {
+		if (dp + 1 == endp) {
+			if (*dp != 0xFF)
+				return(-1);
+			if (textlenp)
+				*textlenp = dp - data;
+			return(0);
+		}
+		if (dp[0] == 0xFF && dp[1] == 0xFF)
+			break;
+		dp += 2;
+	}
+	if (textlenp)
+		*textlenp = dp - data;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_ucs2_81(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp, *endp;
+	unsigned textlen;
+
+	if (nbytes < 4)
+		return(-1);
+	if (!data[1])
+		return(-1);
+	textlen = data[1] + 3;
+	if (textlen > nbytes)
+		return(-1);
+	if (textlenp)
+		*textlenp = textlen;
+	dp = data + textlen;
+	endp = data + nbytes;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_ucs2_82(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp, *endp;
+	unsigned textlen;
+
+	if (nbytes < 5)
+		return(-1);
+	if (!data[1])
+		return(-1);
+	textlen = data[1] + 4;
+	if (textlen > nbytes)
+		return(-1);
+	if (textlenp)
+		*textlenp = textlen;
+	dp = data + textlen;
+	endp = data + nbytes;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_empty(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp;
+	unsigned n;
+
+	dp = data;
+	for (n = 0; n < nbytes; n++)
+		if (*dp++ != 0xFF)
+			return(-1);
+	if (textlenp)
+		*textlenp = 0;
+	return(0);
+}
+
+validate_alpha_field(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	if (data[0] < 0x80)
+		return validate_classic_gsm(data, nbytes, textlenp);
+	switch (data[0]) {
+	case 0x80:
+		return validate_ucs2_80(data, nbytes, textlenp);
+	case 0x81:
+		return validate_ucs2_81(data, nbytes, textlenp);
+	case 0x82:
+		return validate_ucs2_82(data, nbytes, textlenp);
+	case 0xFF:
+		return validate_empty(data, nbytes, textlenp);
+	default:
+		return -1;
+	}
+}
--- a/uicc/dispatch.c	Thu Feb 04 02:55:31 2021 +0000
+++ b/uicc/dispatch.c	Thu Feb 04 03:40:55 2021 +0000
@@ -10,6 +10,8 @@
 
 extern int cmd_exec();
 extern int cmd_iccid();
+extern int cmd_pb_dump();
+extern int cmd_pb_dump_rec();
 extern int cmd_readbin();
 extern int cmd_readrec();
 extern int cmd_select();
@@ -27,6 +29,8 @@
 	{"exec", 1, 1, cmd_exec},
 	{"exit", 0, 0, good_exit},
 	{"iccid", 0, 0, cmd_iccid},
+	{"pb-dump", 1, 2, cmd_pb_dump},
+	{"pb-dump-rec", 2, 3, cmd_pb_dump_rec},
 	{"quit", 0, 0, good_exit},
 	{"readbin", 2, 2, cmd_readbin},
 	{"readrec", 2, 2, cmd_readrec},
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uicc/pbcommon.c	Thu Feb 04 03:40:55 2021 +0000
@@ -0,0 +1,63 @@
+/*
+ * This module implements the common functions for all phonebook commands.
+ */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "file_id.h"
+
+static struct map {
+	char	*user_name;
+	char	*canon_name;
+	int	file_id;
+} phonebook_map[] = {
+	{"adn",		"EF_ADN",	EF_ADN},
+	{"ADN",		"EF_ADN",	EF_ADN},
+	{"EF_ADN",	"EF_ADN",	EF_ADN},
+	{"fdn",		"EF_FDN",	EF_FDN},
+	{"FDN",		"EF_FDN",	EF_FDN},
+	{"EF_FDN",	"EF_FDN",	EF_FDN},
+	{"sdn",		"EF_SDN",	EF_SDN},
+	{"SDN",		"EF_SDN",	EF_SDN},
+	{"EF_SDN",	"EF_SDN",	EF_SDN},
+	{"msisdn",	"EF_MSISDN",	EF_MSISDN},
+	{"MSISDN",	"EF_MSISDN",	EF_MSISDN},
+	{"EF_MSISDN",	"EF_MSISDN",	EF_MSISDN},
+	/* table search terminator */
+	{0,		0,		-1}
+};
+
+phonebook_op_common(reqname, rec_len_ret, rec_count_ret)
+	char *reqname;
+	unsigned *rec_len_ret, *rec_count_ret;
+{
+	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(DF_TELECOM);
+	if (rc < 0)
+		return(rc);
+	rc = select_op(tp->file_id);
+	if (rc < 0)
+		return(rc);
+	rc = select_resp_get_linear_fixed(rec_len_ret, rec_count_ret);
+	if (rc < 0)
+		return(rc);
+	if (rec_len_ret && *rec_len_ret < 14) {
+		fprintf(stderr,
+	"error: %s has record length of %u bytes, less than minimum 14\n",
+			tp->canon_name, *rec_len_ret);
+		return(-1);
+	}
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uicc/pbdump.c	Thu Feb 04 03:40:55 2021 +0000
@@ -0,0 +1,184 @@
+/*
+ * This module implements the pb-dump command.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pcsclite.h>
+#include <winscard.h>
+#include "globals.h"
+
+static char gsm_address_digits[16] =
+	{'0','1','2','3','4','5','6','7','8','9','*','#','a','b','c','?'};
+
+static
+check_all_blank()
+{
+	u_char *dp, *endp;
+
+	dp = sim_resp_data;
+	endp = sim_resp_data + sim_resp_data_len;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(0);
+	return(1);
+}
+
+static
+decode_number(data, nbytes, out)
+	u_char *data;
+	unsigned nbytes;
+	char *out;
+{
+	u_char *dp, *endp;
+	int c;
+
+	dp = data;
+	endp = data + nbytes;
+	while (dp < endp) {
+		c = *dp & 0xF;
+		if (c == 0xF)
+			return(-1);
+		*out++ = gsm_address_digits[c];
+		c = *dp >> 4;
+		if (c == 0xF) {
+			if (dp + 1 == endp)
+				break;
+			else
+				return(-1);
+		}
+		*out++ = gsm_address_digits[c];
+		dp++;
+	}
+	*out = '\0';
+	return(0);
+}
+
+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, record_len, outf)
+	unsigned recno, record_len;
+	FILE *outf;
+{
+	int rc;
+	unsigned textlen;
+	u_char *fixp;
+	char digits[21];
+
+	fprintf(outf, "#%u: ", recno);
+	if (record_len > 14) {
+		rc = validate_alpha_field(sim_resp_data,
+					  record_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_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)
+	char **argv;
+{
+	int rc;
+	FILE *outf;
+	unsigned recno;
+	unsigned record_len, record_count;
+
+	rc = phonebook_op_common(argv[1], &record_len, &record_count);
+	if (rc < 0)
+		return(rc);
+	if (argv[2]) {
+		outf = fopen(argv[2], "w");
+		if (!outf) {
+			perror(argv[2]);
+			return(-1);
+		}
+	} else
+		outf = stdout;
+	for (recno = 1; recno <= record_count; recno++) {
+		rc = readrec_op(recno, 0x04, record_len);
+		if (rc < 0) {
+			if (argv[2])
+				fclose(outf);
+			return(rc);
+		}
+		if (check_all_blank())
+			continue;
+		dump_record(recno, record_len, outf);
+	}
+	if (argv[2])
+		fclose(outf);
+	return(0);
+}
+
+cmd_pb_dump_rec(argc, argv)
+	char **argv;
+{
+	int rc;
+	unsigned recno, startrec, endrec;
+	unsigned record_len, record_count;
+
+	rc = phonebook_op_common(argv[1], &record_len, &record_count);
+	if (rc < 0)
+		return(rc);
+	startrec = strtoul(argv[2], 0, 0);
+	if (startrec < 1 || startrec > 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 > 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, record_len);
+		if (rc < 0)
+			return(rc);
+		if (check_all_blank())
+			continue;
+		dump_record(recno, record_len, stdout);
+	}
+	return(0);
+}