From 5714dbfa08cd5f77312d5dbcb06012ff992ed450 Mon Sep 17 00:00:00 2001 From: Peter Marschall Date: Sun, 12 Jan 2020 10:40:05 +0100 Subject: [PATCH] opensc-explorer: add function 'get_record' --- doc/tools/opensc-explorer.1.xml | 25 +++++++++ src/tools/opensc-explorer.c | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/doc/tools/opensc-explorer.1.xml b/doc/tools/opensc-explorer.1.xml index 30bc3057..8632f8de 100644 --- a/doc/tools/opensc-explorer.1.xml +++ b/doc/tools/opensc-explorer.1.xml @@ -403,6 +403,31 @@ + + + get_record + file-id + rec-no + output + + + + Copy a record of a record-oriented EF to a local file. + The local file is specified by + output + while the card file and the record are specified by + file-id and + rec-no, + + + If output is omitted, + the name of the output file will be derived from the + full card path to file-id. + and the rec-no. + + + + help diff --git a/src/tools/opensc-explorer.c b/src/tools/opensc-explorer.c index 16ef9d17..d2c7d5ca 100644 --- a/src/tools/opensc-explorer.c +++ b/src/tools/opensc-explorer.c @@ -100,6 +100,7 @@ static int do_verify(int argc, char **argv); static int do_change(int argc, char **argv); static int do_unblock(int argc, char **argv); static int do_get(int argc, char **argv); +static int do_get_record(int argc, char **argv); static int do_update_binary(int argc, char **argv); static int do_update_record(int argc, char **argv); static int do_put(int argc, char **argv); @@ -174,6 +175,9 @@ static struct command cmds[] = { { do_get, "get", " []", "copy an EF to a local file" }, + { do_get_record, + "get_record", " []", + "copy a record of an EF to a local file" }, { do_get_data, "do_get", " []", "get a data object" }, @@ -1474,6 +1478,92 @@ err: return -err; } +static int do_get_record(int argc, char **argv) +{ + u8 buf[SC_MAX_EXT_APDU_RESP_SIZE]; + int r, err = 1; + size_t rec, count = 0; + sc_path_t path; + sc_file_t *file = NULL; + char *filename; + FILE *outf = NULL; + + if (argc < 2 || argc > 3) + return usage(do_get); + if (arg_to_path(argv[0], &path, 0) != 0) + return usage(do_get); + rec = strtoul(argv[1], NULL, 10); + + filename = (argc == 3) ? argv[2] : path_to_filename(&path, '_', rec); + outf = (strcmp(filename, "-") == 0) + ? stdout + : fopen(filename, "wb"); + if (outf == NULL) { + perror(filename); + goto err; + } + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); + if (r || file == NULL) { + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); + goto err; + } + + /* fail on wrong file type */ + if (file->type != SC_FILE_TYPE_WORKING_EF || + (file->ef_structure != SC_FILE_EF_LINEAR_FIXED && + file->ef_structure != SC_FILE_EF_LINEAR_FIXED_TLV && + file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE && + file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE_TLV)) { + fprintf(stderr, "Only record-oriented working EFs may be read\n"); + goto err; + } + + /* fail on out of bound record index: + * must be > 0 and if record_count is set <= record_count */ + if (rec < 1 || (file->record_count > 0 && rec > file->record_count)) { + fprintf(stderr, "Invalid record number %"SC_FORMAT_LEN_SIZE_T"u\n", rec); + goto err; + } + + r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + if (r < 0) { + fprintf(stderr, "Cannot read record %"SC_FORMAT_LEN_SIZE_T"u; return %i\n", rec, r); + goto err; + } + + count = r; + + if (count > 0) { + size_t written = fwrite(buf, 1, (size_t) r, outf); + + if (written != count) { + fprintf(stderr, "Cannot write to file %s (only %"SC_FORMAT_LEN_SIZE_T"u of %"SC_FORMAT_LEN_SIZE_T"u bytes written)", + filename, written, count); + goto err; + } + } + + if (outf == stdout) { + fwrite("\n", 1, 1, outf); + } + else { + printf("Total of %"SC_FORMAT_LEN_SIZE_T"u bytes read from %s and saved to %s.\n", + count, argv[0], filename); + } + + err = 0; +err: + sc_file_free(file); + if (outf != NULL && outf != stdout) + fclose(outf); + select_current_path_or_die(); + return -err; +} + static int do_update_binary(int argc, char **argv) { u8 buf[SC_MAX_EXT_APDU_DATA_SIZE];