opensc/src/tools/opensc-explorer.c

2005 lines
44 KiB
C

/*
* opensc-explorer.c: A shell for accessing smart cards with libopensc
*
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef ENABLE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#if !defined(_WIN32)
#include <arpa/inet.h> /* for htons() */
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#include "libopensc/opensc.h"
#include "libopensc/asn1.h"
#include "libopensc/cardctl.h"
#include "libopensc/cards.h"
#include "common/compat_strlcpy.h"
#include "common/compat_getopt.h"
#include "util.h"
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
/* type for associations of IDs to names */
typedef struct _id2str {
unsigned int id;
const char *str;
} id2str_t;
static const char *app_name = "opensc-explorer";
static int opt_wait = 0, verbose = 0;
static const char *opt_driver = NULL;
static const char *opt_reader = NULL;
static const char *opt_startfile = NULL;
static sc_file_t *current_file = NULL;
static sc_path_t current_path;
static sc_context_t *ctx = NULL;
static sc_card_t *card = NULL;
static const struct option options[] = {
{ "reader", 1, NULL, 'r' },
{ "card-driver", 1, NULL, 'c' },
{ "mf", 1, NULL, 'm' },
{ "wait", 0, NULL, 'w' },
{ "verbose", 0, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
static const char *option_help[] = {
"Uses reader number <arg> [0]",
"Forces the use of driver <arg> [auto-detect]",
"Selects path <arg> on start-up, or none if empty [3F00]",
"Wait for card insertion",
"Verbose operation. Use several times to enable debug output.",
};
/* declare functions called by user commands */
static int do_echo(int argc, char **argv);
static int do_ls(int argc, char **argv);
static int do_find(int argc, char **argv);
static int do_find_tags(int argc, char **argv);
static int do_cd(int argc, char **argv);
static int do_cat(int argc, char **argv);
static int do_info(int argc, char **argv);
static int do_create(int argc, char **argv);
static int do_mkdir(int argc, char **argv);
static int do_delete(int argc, char **argv);
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_update_binary(int argc, char **argv);
static int do_update_record(int argc, char **argv);
static int do_put(int argc, char **argv);
static int do_debug(int argc, char **argv);
static int do_erase(int argc, char **argv);
static int do_random(int argc, char **argv);
static int do_get_data(int argc, char **argv);
static int do_put_data(int argc, char **argv);
static int do_apdu(int argc, char **argv);
static int do_sm(int argc, char **argv);
static int do_asn1(int argc, char **argv);
static int do_help(int argc, char **argv);
static int do_quit(int argc, char **argv);
struct command {
int (*func)(int, char **);
const char * name;
const char * args;
const char * help;
};
static struct command cmds[] = {
{ do_echo,
"echo", "[<string> ..]",
"display arguments" },
{ do_ls,
"ls", "[<pattern> ..]",
"list files in the current DF" },
{ do_find,
"find", "[<start id> [<end id>]]",
"find all files in the current DF" },
{ do_find_tags,
"find_tags", "[<start tag> [<end tag>]]",
"find all tags of data objects in the current context" },
{ do_cd,
"cd", "{.. | <file id> | aid:<DF name>}",
"change to another DF" },
{ do_cat,
"cat", "[<file id> | sfi:<sfi id>]"
, "print the contents of an EF" },
{ do_info,
"info", "[<file id>]",
"display attributes of card file" },
{ do_create,
"create", "<file id> <size>",
"create a new EF" },
{ do_mkdir,
"mkdir", "<file id> <size>",
"create a new DF" },
{ do_delete,
"delete", "<file id>",
"remove an EF/DF" },
{ do_delete,
"rm", "<file id>",
"remove an EF/DF" },
{ do_verify,
"verify", "{CHV|KEY|AUT|PRO}<key ref> [<pin>]",
"present a PIN or key to the card" },
{ do_change,
"change", "CHV<pin ref> [[<old pin>] <new pin>]",
"change a PIN" },
{ do_unblock,
"unblock", "CHV<pin ref> [<puk> [<new pin>]]",
"unblock a PIN" },
{ do_put,
"put", "<file id> [<input file>]",
"copy a local file to the card" },
{ do_get,
"get", "<file id> [<output file>]",
"copy an EF to a local file" },
{ do_get_data,
"do_get", "<hex tag> [<output file>]",
"get a data object" },
{ do_put_data,
"do_put", "<hex tag> <data>",
"put a data object" },
{ do_erase,
"erase", "",
"erase card" },
{ do_random,
"random", "<count>",
"obtain <count> random bytes from card" },
{ do_update_record,
"update_record", "<file id> <rec no> <rec offs> <data>",
"update record" },
{ do_update_binary,
"update_binary", "<file id> <offs> <data>",
"update binary" },
{ do_apdu,
"apdu", "<data>+",
"send a custom apdu command" },
{ do_asn1,
"asn1", "[<file id>]",
"decode an ASN.1 file" },
{ do_sm,
"sm", "open|close",
"call SM 'open' or 'close' handlers, if available"},
{ do_debug,
"debug", "[<value>]",
"get/set the debug level" },
{ do_quit,
"quit", "",
"quit this program" },
{ do_quit,
"exit", "",
"quit this program" },
{ do_help,
"help", "",
"show this help" },
{ NULL, NULL, NULL, NULL }
};
static char *path_to_filename(const sc_path_t *path, const char sep)
{
static char buf[2*SC_MAX_PATH_STRING_SIZE];
size_t i, j;
for (i = 0, j = 0; path != NULL && i < path->len; i++) {
if (sep != '\0' && i > 0 && (i & 1) == 0)
j += sprintf(buf+j, "%c", sep);
j += sprintf(buf+j, "%02X", path->value[i]);
}
buf[j] = '\0';
return buf;
}
static int parse_string_or_hexdata(const char *in, u8 *out, size_t *outlen)
{
if (in == NULL)
return SC_ERROR_INVALID_ARGUMENTS;
if (*in == '"') {
u8 quote = *in++;
size_t count = 0;
while (*in != quote && *in != '\0' && count < *outlen)
out[count++] = *in++;
if (*in == '\0')
return SC_ERROR_INVALID_ARGUMENTS;
if (count >= *outlen)
return SC_ERROR_BUFFER_TOO_SMALL;
*outlen = count;
return 0;
}
else
return sc_hex_to_bin(in, out, outlen);
}
static int usage(int (*func)(int, char **))
{
struct command *cmd;
for (cmd = cmds; cmd->func; cmd++)
if (cmd->func == func)
printf("Usage: %s %s\n", cmd->name, cmd->args);
return -1;
}
static void die(int ret)
{
sc_file_free(current_file);
if (card) {
sc_unlock(card);
sc_disconnect_card(card);
}
if (ctx)
sc_release_context(ctx);
exit(ret);
}
static void select_current_path_or_die(void)
{
if (current_path.type || current_path.len) {
int r = sc_select_file(card, &current_path, NULL);
if (r) {
printf("unable to select parent DF: %s\n", sc_strerror(r));
die(1);
}
}
}
static struct command *
ambiguous_match(struct command *table, const char *cmd)
{
struct command *last_match = NULL;
int matches = 0;
for (; table->name; table++) {
if (strncasecmp(cmd, table->name, strlen(cmd)) == 0) {
last_match = table;
matches++;
}
}
if (matches > 1) {
printf("Ambiguous command: %s\n", cmd);
return NULL;
}
return last_match;
}
static void
check_ret(int r, int op, const char *err, const sc_file_t *file)
{
fprintf(stderr, "%s: %s\n", err, sc_strerror(r));
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)
fprintf(stderr, "ACL for operation: %s\n", util_acl_to_str(sc_file_get_acl_entry(file, op)));
}
static int
arg_to_fid(const char *arg, u8 *fid)
{
unsigned int fid0, fid1;
if (strlen(arg) != 4) {
printf("Wrong ID length.\n");
return -1;
}
if (sscanf(arg, "%02X%02X", &fid0, &fid1) != 2) {
printf("Invalid ID.\n");
return -1;
}
fid[0] = (unsigned char)fid0;
fid[1] = (unsigned char)fid1;
return 0;
}
static int
arg_to_path(const char *arg, sc_path_t *path, int is_id)
{
memset(path, 0, sizeof(sc_path_t));
if (strncasecmp(arg, "aid:", strlen("aid:")) == 0) {
/* DF aid */
const char *p = arg + strlen("aid:");
int r;
path->type = SC_PATH_TYPE_DF_NAME;
path->len = sizeof(path->value);
if ((r = sc_hex_to_bin(p, path->value, &path->len)) < 0) {
printf("Error parsing AID: %s\n", p);
return r;
}
} else {
/* file id */
u8 cbuf[2];
if (arg_to_fid(arg, cbuf) < 0)
return -1;
if ((cbuf[0] == 0x3F && cbuf[1] == 0x00) || is_id) {
path->len = 2;
memcpy(path->value, cbuf, 2);
path->type = (is_id) ? SC_PATH_TYPE_FILE_ID : SC_PATH_TYPE_PATH;
} else {
*path = current_path;
if (path->type == SC_PATH_TYPE_DF_NAME) {
if (path->len > sizeof(path->aid.value)) {
printf("Invalid length of DF_NAME path\n");
return -1;
}
memcpy(path->aid.value, path->value, path->len);
path->aid.len = path->len;
path->type = SC_PATH_TYPE_FILE_ID;
path->len = 0;
}
sc_append_path_id(path, cbuf, 2);
}
}
return 0;
}
static void print_file(const sc_file_t *file)
{
const char *format = " %02X%02X ";
const char *st = NULL;
if(!file->type_attr_len)
st = "???";
switch (file->type) {
case SC_FILE_TYPE_WORKING_EF:
st = "wEF";
break;
case SC_FILE_TYPE_INTERNAL_EF:
st = "iEF";
break;
case SC_FILE_TYPE_DF:
format = "[%02X%02X]";
st = "DF";
break;
}
printf(format, file->id >> 8, file->id & 0xFF);
if (st == NULL)
printf("\t0x%02X", *file->type_attr);
else
printf("\t%4s", st);
printf(" %5lu", (unsigned long)file->size);
if (file->namelen) {
printf("\tName: ");
util_print_binary(stdout, file->name, file->namelen);
}
printf("\n");
return;
}
static int do_echo(int argc, char **argv)
{
int i;
for (i = 0; i < argc; i++) {
printf("%s%s", argv[i], (i < argc) ? " " : "");
}
printf("\n");
return 0;
}
static int pattern_match(const char *pattern, const char *string)
{
if (pattern == NULL || string == NULL)
return 0;
while (*pattern != '\0' && *string != '\0') {
/* wildcard matching multple characters */
if (*pattern == '*') {
for (pattern++; *string != '\0' ; string++)
if (pattern_match(pattern, string))
return 1;
return 0;
}
/* simple character class matching a single character */
else if (*pattern == '[') {
char *end = strchr(pattern, ']');
int match = 0;
for (pattern++; end != NULL && pattern != end; pattern++) {
if (tolower(*pattern) == tolower(*string))
match++;
}
if (!match)
return 0;
pattern++;
string++;
}
/* single character comparison / wildcard matching a single character */
else if (tolower(*pattern) == tolower(*string) || *pattern == '?') {
pattern++;
string++;
}
else
return 0;
if (*string == '\0' || *pattern == '\0')
break;
}
return (*pattern != '\0' || *string != '\0' || tolower(*pattern) != tolower(*string)) ? 0 : 1;
}
static int do_ls(int argc, char **argv)
{
u8 buf[256], *cur = buf;
int r, count;
r = sc_list_files(card, buf, sizeof(buf));
if (r < 0) {
check_ret(r, SC_AC_OP_LIST_FILES, "unable to receive file listing", current_file);
return -1;
}
count = r;
printf("FileID\tType Size\n");
while (count >= 2) {
sc_path_t path;
sc_file_t *file = NULL;
char filename[10];
int i = 0;
int matches = 0;
/* construct file name */
sprintf(filename, "%02X%02X", cur[0], cur[1]);
/* compare file name against patterns */
for (i = 0; i < argc; i++) {
if (pattern_match(argv[i], filename)) {
matches = 1;
break;
}
}
/* if any filename pattern were given, filter only matching file names */
if (argc == 0 || matches) {
if (current_path.type != SC_PATH_TYPE_DF_NAME) {
path = current_path;
sc_append_path_id(&path, cur, 2);
} else {
if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, cur, 2, 0, 0) != SC_SUCCESS) {
printf("unable to set path.\n");
die(1);
}
}
r = sc_select_file(card, &path, &file);
if (r) {
printf(" %02X%02X unable to select file, %s\n", cur[0], cur[1], sc_strerror(r));
} else {
file->id = (cur[0] << 8) | cur[1];
print_file(file);
sc_file_free(file);
}
}
cur += 2;
count -= 2;
select_current_path_or_die();
}
return 0;
}
static int do_find(int argc, char **argv)
{
u8 fid[2], end[2];
sc_path_t path;
int r;
fid[0] = 0;
fid[1] = 0;
end[0] = 0xFF;
end[1] = 0xFF;
switch (argc) {
case 2:
if (arg_to_fid(argv[1], end) != 0)
return usage(do_find);
/* fall through */
case 1:
if (arg_to_fid(argv[0], fid) != 0)
return usage(do_find);
/* fall through */
case 0:
break;
default:
return usage(do_find);
}
printf("FileID\tType Size\n");
while (1) {
sc_file_t *file = NULL;
printf("(%02X%02X)\r", fid[0], fid[1]);
fflush(stdout);
if (current_path.type != SC_PATH_TYPE_DF_NAME) {
path = current_path;
sc_append_path_id(&path, fid, sizeof fid);
} else {
if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, 0) != SC_SUCCESS) {
printf("unable to set path.\n");
die(1);
}
}
r = sc_select_file(card, &path, &file);
switch (r) {
case SC_SUCCESS:
file->id = (fid[0] << 8) | fid[1];
print_file(file);
sc_file_free(file);
select_current_path_or_die();
break;
case SC_ERROR_NOT_ALLOWED:
case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED:
printf("(%02X%02X)\t%s\n", fid[0], fid[1], sc_strerror(r));
break;
}
if (fid[0] == end[0] && fid[1] == end[1])
break;
fid[1] = fid[1] + 1;
if (fid[1] == 0)
fid[0] = fid[0] + 1;
}
return 0;
}
static int do_find_tags(int argc, char **argv)
{
u8 start[2], end[2], rbuf[256];
int r;
unsigned int tag, tag_end;
start[0] = 0x00;
start[1] = 0x00;
end[0] = 0xFF;
end[1] = 0xFF;
switch (argc) {
case 2:
if (arg_to_fid(argv[1], end) != 0)
return usage(do_find_tags);
/* fall through */
case 1:
if (arg_to_fid(argv[0], start) != 0)
return usage(do_find_tags);
/* fall through */
case 0:
break;
default:
return usage(do_find_tags);
}
tag = (start[0] << 8) | start[1];
tag_end = (end[0] << 8) | end[1];
printf("Tag\tType\n");
while (1) {
printf("(%04X)\r", tag);
fflush(stdout);
r = sc_get_data(card, tag, rbuf, sizeof rbuf);
if (r >= 0) {
printf(" %04X ", tag);
if (tag == 0)
printf("\tdump file");
if ((0x0001 <= tag && tag <= 0x00FE)
|| (0x1F1F <= tag && tag <= 0xFFFF))
printf("\tBER-TLV");
if (tag == 0x00FF || tag == 0x02FF)
printf("\tspecial function");
if (0x0100 <= tag && tag <= 0x01FF)
printf("\tproprietary");
if (tag == 0x0200)
printf("\tRFU");
if (0x0201 <= tag && tag <= 0x02FE)
printf("\tSIMPLE-TLV");
printf("\n");
if (r > 0)
util_hex_dump_asc(stdout, rbuf, r, -1);
} else {
switch (r) {
case SC_ERROR_NOT_ALLOWED:
case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED:
printf("(%04X)\t%s\n", tag, sc_strerror(r));
break;
}
}
if (tag >= tag_end)
break;
tag++;
}
return 0;
}
static int do_cd(int argc, char **argv)
{
sc_path_t path;
sc_file_t *file;
int r;
if (argc != 1)
return usage(do_cd);
if (strcmp(argv[0], "..") == 0) {
path = current_path;
if (path.len < 4) {
printf("unable to go up, already in MF.\n");
return -1;
}
if (path.type == SC_PATH_TYPE_DF_NAME) {
sc_format_path("3F00", &path);
}
else {
path.len -= 2;
}
r = sc_select_file(card, &path, &file);
if (r) {
printf("unable to go up: %s\n", sc_strerror(r));
return -1;
}
sc_file_free(current_file);
current_file = file;
current_path = path;
return 0;
}
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_cd);
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select DF", current_file);
return -1;
}
if ((file->type != SC_FILE_TYPE_DF) && (card->type != SC_CARD_TYPE_BELPIC_EID)) {
printf("Error: file is not a DF.\n");
sc_file_free(file);
select_current_path_or_die();
return -1;
}
current_path = path;
sc_file_free(current_file);
current_file = file;
return 0;
}
static int read_and_util_print_binary_file(sc_file_t *file)
{
unsigned char *buf = NULL;
int r;
size_t size;
if (file->size) {
size = file->size;
} else {
size = 1024;
}
buf = malloc(size);
if (!buf)
return -1;
r = sc_read_binary(card, 0, buf, size, 0);
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", file);
return -1;
}
if ((r == 0) && (card->type == SC_CARD_TYPE_BELPIC_EID))
return -1;
util_hex_dump_asc(stdout, buf, r, 0);
free(buf);
return 0;
}
static int read_and_print_record_file(sc_file_t *file, unsigned char sfi)
{
u8 buf[256];
int rec, r;
for (rec = 1; ; rec++) {
r = sc_read_record(card, rec, buf, sizeof(buf),
SC_RECORD_BY_REC_NR | sfi);
if (r == SC_ERROR_RECORD_NOT_FOUND)
return 0;
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", file);
return -1;
}
printf("Record %d:\n", rec);
util_hex_dump_asc(stdout, buf, r, 0);
}
}
static int do_cat(int argc, char **argv)
{
int r, err = 1;
sc_path_t path;
sc_file_t *file = NULL;
int not_current = 1;
int sfi = 0;
if (argc > 1)
return usage(do_cat);
if (!argc) {
path = current_path;
file = current_file;
not_current = 0;
} else {
const char sfi_prefix[] = "sfi:";
if (strncasecmp(argv[0], sfi_prefix, strlen(sfi_prefix)) == 0) {
const char *sfi_n = argv[0] + strlen(sfi_prefix);
if(!current_file) {
printf("A DF must be selected to read by SFI\n");
goto err;
}
path = current_path;
file = current_file;
not_current = 0;
sfi = atoi(sfi_n);
if ((sfi < 1) || (sfi > 30)) {
printf("Invalid SFI: %s\n", sfi_n);
return usage(do_cat);
}
} else {
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_cat);
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select file",
current_file);
goto err;
}
}
}
if (file->type != SC_FILE_TYPE_WORKING_EF &&
!(file->type == SC_FILE_TYPE_DF && sfi)) {
printf("only working EFs may be read\n");
goto err;
}
if (file->ef_structure == SC_FILE_EF_TRANSPARENT && !sfi)
read_and_util_print_binary_file(file);
else
read_and_print_record_file(file, sfi);
err = 0;
err:
if (not_current) {
sc_file_free(file);
select_current_path_or_die();
}
return -err;
}
static int do_info(int argc, char **argv)
{
sc_file_t *file;
sc_path_t path;
size_t i;
const char *st = NULL;
int r, not_current = 1;
const id2str_t *ac_ops = NULL;
if (!argc) {
path = current_path;
file = current_file;
not_current = 0;
} else if (argc == 1) {
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_info);
r = sc_select_file(card, &path, &file);
if (r) {
printf("unable to select file: %s\n", sc_strerror(r));
return -1;
}
} else
return usage(do_info);
if(!file->type_attr_len)
st = "Unknown File";
switch (file->type) {
case SC_FILE_TYPE_WORKING_EF:
case SC_FILE_TYPE_INTERNAL_EF:
st = "Elementary File";
break;
case SC_FILE_TYPE_DF:
st = "Dedicated File";
break;
}
if (st == NULL)
printf("\nFile type [%02x] ID %04X", *file->type_attr, file->id);
else
printf("\n%s ID %04X", st, file->id);
if (file->sid)
printf(", SFI %02X", file->sid);
printf("\n\n%-15s%s\n", "File path:", path_to_filename(&path, '/'));
printf("%-15s%lu bytes\n", "File size:", (unsigned long) file->size);
if (file->type == SC_FILE_TYPE_DF) {
static const id2str_t ac_ops_df[] = {
{ SC_AC_OP_SELECT, "SELECT" },
{ SC_AC_OP_LOCK, "LOCK" },
{ SC_AC_OP_DELETE, "DELETE" },
{ SC_AC_OP_CREATE, "CREATE" },
{ SC_AC_OP_REHABILITATE, "REHABILITATE" },
{ SC_AC_OP_INVALIDATE, "INVALIDATE" },
{ SC_AC_OP_LIST_FILES, "LIST FILES" },
{ SC_AC_OP_CRYPTO, "CRYPTO" },
{ SC_AC_OP_DELETE_SELF, "DELETE SELF" },
{ 0, NULL }
};
if (file->namelen) {
printf("%-15s", "DF name:");
util_print_binary(stdout, file->name, file->namelen);
printf("\n");
}
ac_ops = ac_ops_df;
} else {
static const id2str_t ac_ops_ef[] = {
{ SC_AC_OP_READ, "READ" },
{ SC_AC_OP_UPDATE, "UPDATE" },
{ SC_AC_OP_DELETE, "DELETE" },
{ SC_AC_OP_WRITE, "WRITE" },
{ SC_AC_OP_REHABILITATE, "REHABILITATE" },
{ SC_AC_OP_INVALIDATE, "INVALIDATE" },
{ SC_AC_OP_LIST_FILES, "LIST FILES" },
{ SC_AC_OP_CRYPTO, "CRYPTO" },
{ 0, NULL }
};
const id2str_t ef_type_name[] = {
{ SC_FILE_EF_TRANSPARENT, "Transparent" },
{ SC_FILE_EF_LINEAR_FIXED, "Linear fixed" },
{ SC_FILE_EF_LINEAR_FIXED_TLV, "Linear fixed, SIMPLE-TLV" },
{ SC_FILE_EF_LINEAR_VARIABLE, "Linear variable" },
{ SC_FILE_EF_LINEAR_VARIABLE_TLV, "Linear variable, SIMPLE-TLV" },
{ SC_FILE_EF_CYCLIC, "Cyclic" },
{ SC_FILE_EF_CYCLIC_TLV, "Cyclic, SIMPLE-TLV" },
{ 0, NULL }
};
const char *ef_type = "Unknown";
for (i = 0; ef_type_name[i].str != NULL; i++)
if (file->ef_structure == ef_type_name[i].id)
ef_type = ef_type_name[i].str;
printf("%-15s%s\n", "EF structure:", ef_type);
ac_ops = ac_ops_ef;
}
for (i = 0; ac_ops != NULL && ac_ops[i].str != NULL; i++) {
int len = strlen(ac_ops[i].str);
printf("ACL for %s:%*s %s\n",
ac_ops[i].str,
(12 > len) ? (12 - len) : 0, "",
util_acl_to_str(sc_file_get_acl_entry(file, ac_ops[i].id)));
}
if (file->prop_attr_len) {
printf("%-25s", "Proprietary attributes:");
util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, " ");
printf("\n");
}
if (file->sec_attr_len) {
printf("%-25s", "Security attributes:");
util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, " ");
printf("\n");
}
printf("\n");
if (not_current) {
sc_file_free(file);
select_current_path_or_die();
}
return 0;
}
static int create_file(sc_file_t *file)
{
int r;
r = sc_create_file(card, file);
if (r) {
check_ret(r, SC_AC_OP_CREATE, "CREATE FILE failed", current_file);
return -1;
}
/* Make sure we're back in the parent directory, because on some cards
* CREATE FILE also selects the newly created file. */
select_current_path_or_die();
return 0;
}
static int do_create(int argc, char **argv)
{
sc_path_t path;
sc_file_t *file;
unsigned int size;
int r, op;
if (argc < 2)
return usage(do_create);
if (arg_to_path(argv[0], &path, 1) != 0)
return usage(do_create);
/* %z isn't supported everywhere */
if (sscanf(argv[1], "%u", &size) != 1)
return usage(do_create);
file = sc_file_new();
file->id = (path.value[0] << 8) | path.value[1];
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
file->size = (size_t) size;
file->status = SC_FILE_STATUS_ACTIVATED;
for (op = 0; op < SC_MAX_AC_OPS; op++)
sc_file_add_acl_entry(file, op, SC_AC_NONE, 0);
if (argc > 2) {
snprintf((char *)file->name, sizeof(file->name), "%s", argv[2]);
file->namelen = strlen((char *)file->name);
}
r = create_file(file);
sc_file_free(file);
return r;
}
static int do_mkdir(int argc, char **argv)
{
sc_path_t path;
sc_file_t *file;
unsigned int size;
int r, op;
if (argc != 2)
return usage(do_mkdir);
if (arg_to_path(argv[0], &path, 1) != 0)
return usage(do_mkdir);
if (sscanf(argv[1], "%u", &size) != 1)
return usage(do_mkdir);
file = sc_file_new();
file->id = (path.value[0] << 8) | path.value[1];
file->type = SC_FILE_TYPE_DF;
file->size = size;
file->status = SC_FILE_STATUS_ACTIVATED;
for (op = 0; op < SC_MAX_AC_OPS; op++)
sc_file_add_acl_entry(file, op, SC_AC_NONE, 0);
r = create_file(file);
sc_file_free(file);
return r;
}
static int do_delete(int argc, char **argv)
{
sc_path_t path;
int r;
if (argc != 1)
return usage(do_delete);
if (arg_to_path(argv[0], &path, 1) != 0)
return usage(do_delete);
if (path.len != 2)
return usage(do_delete);
path.type = SC_PATH_TYPE_FILE_ID;
r = sc_delete_file(card, &path);
if (r) {
check_ret(r, SC_AC_OP_DELETE, "DELETE FILE failed", current_file);
return -1;
}
return 0;
}
static int do_verify(int argc, char **argv)
{
const id2str_t typeNames[] = {
{ SC_AC_CHV, "CHV" },
{ SC_AC_AUT, "KEY" },
{ SC_AC_AUT, "AUT" },
{ SC_AC_PRO, "PRO" },
{ SC_AC_NONE, NULL, }
};
int r, tries_left = -1;
u8 buf[64];
size_t buflen = sizeof(buf), i;
struct sc_pin_cmd_data data;
int prefix_len = 0;
if (argc < 1 || argc > 2)
return usage(do_verify);
memset(&data, 0, sizeof(data));
data.cmd = SC_PIN_CMD_VERIFY;
data.pin_type = SC_AC_NONE;
for (i = 0; typeNames[i].str; i++) {
prefix_len = strlen(typeNames[i].str);
if (strncasecmp(argv[0], typeNames[i].str, prefix_len) == 0) {
data.pin_type = typeNames[i].id;
break;
}
}
if (data.pin_type == SC_AC_NONE) {
printf("Invalid type.\n");
return usage(do_verify);
}
if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) {
printf("Invalid key reference.\n");
return usage(do_verify);
}
if (argc < 2) {
if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) {
printf("Please enter PIN on the reader's pin pad.\n");
data.pin1.prompt = "Please enter PIN";
data.flags |= SC_PIN_CMD_USE_PINPAD;
}
else {
char *pin = NULL;
size_t len = 0;
printf("Please enter PIN: ");
r = util_getpass(&pin, &len, stdin);
if (r < 0) {
printf("util_getpass error.\n");
return -1;
}
if (strlcpy((char *)buf, pin, sizeof(buf)) >= sizeof(buf)) {
free(pin);
printf("PIN too long - aborting VERIFY.\n");
return -1;
}
free(pin);
data.pin1.data = buf;
data.pin1.len = strlen((char *)buf);
}
} else {
r = parse_string_or_hexdata(argv[1], buf, &buflen);
if (0 != r) {
printf("Invalid key value.\n");
return usage(do_verify);
}
data.pin1.data = buf;
data.pin1.len = buflen;
}
r = sc_pin_cmd(card, &data, &tries_left);
if (r) {
if (r == SC_ERROR_PIN_CODE_INCORRECT) {
if (tries_left >= 0)
printf("Incorrect code, %d tries left.\n", tries_left);
else
printf("Incorrect code.\n");
} else
printf("Unable to verify PIN code: %s\n", sc_strerror(r));
return -1;
}
printf("Code correct.\n");
return 0;
}
static int do_change(int argc, char **argv)
{
int ref, r, tries_left = -1;
u8 oldpin[64];
u8 newpin[64];
size_t oldpinlen = 0;
size_t newpinlen = 0;
struct sc_pin_cmd_data data;
memset(&data, 0, sizeof(data));
data.cmd = SC_PIN_CMD_CHANGE;
if (argc < 1 || argc > 3)
return usage(do_change);
if (strncasecmp(argv[0], "CHV", 3)) {
printf("Invalid type.\n");
return usage(do_change);
}
if (sscanf(argv[0] + 3, "%d", &ref) != 1) {
printf("Invalid key reference.\n");
return usage(do_change);
}
if (argc == 3) {
oldpinlen = sizeof(oldpin);
if (parse_string_or_hexdata(argv[1], oldpin, &oldpinlen) != 0) {
printf("Invalid key value.\n");
return usage(do_change);
}
}
if (argc >= 2) {
newpinlen = sizeof(newpin);
if (parse_string_or_hexdata(argv[argc-1], newpin, &newpinlen) != 0) {
printf("Invalid key value.\n");
return usage(do_change);
}
}
data.pin_type = SC_AC_CHV;
data.pin_reference = ref;
data.pin1.data = oldpinlen ? oldpin : NULL;
data.pin1.len = oldpinlen;
data.pin2.data = newpinlen ? newpin : NULL;
data.pin2.len = newpinlen;
r = sc_pin_cmd(card, &data, &tries_left);
if (r) {
if (r == SC_ERROR_PIN_CODE_INCORRECT) {
if (tries_left >= 0)
printf("Incorrect code, %d tries left.\n", tries_left);
else
printf("Incorrect code.\n");
}
printf("Unable to change PIN code: %s\n", sc_strerror(r));
return -1;
}
printf("PIN changed.\n");
return 0;
}
static int do_unblock(int argc, char **argv)
{
int ref, r;
u8 puk[64];
u8 newpin[64];
size_t puklen = 0;
size_t newpinlen = 0;
struct sc_pin_cmd_data data;
memset(&data, 0, sizeof(data));
data.cmd = SC_PIN_CMD_UNBLOCK;
if (argc < 1 || argc > 3)
return usage(do_unblock);
if (strncasecmp(argv[0], "CHV", 3)) {
printf("Invalid type.\n");
return usage(do_unblock);
}
if (sscanf(argv[0] + 3, "%d", &ref) != 1) {
printf("Invalid key reference.\n");
return usage(do_unblock);
}
if (argc > 1) {
puklen = sizeof(puk);
if (parse_string_or_hexdata(argv[1], puk, &puklen) != 0) {
printf("Invalid key value.\n");
return usage(do_unblock);
}
}
if (argc > 2) {
newpinlen = sizeof(newpin);
if (parse_string_or_hexdata(argv[2], newpin, &newpinlen) != 0) {
printf("Invalid key value.\n");
return usage(do_unblock);
}
}
data.pin_type = SC_AC_CHV;
data.pin_reference = ref;
data.pin1.data = puklen ? puk : NULL;
data.pin1.len = puklen;
data.pin2.data = newpinlen ? newpin : NULL;
data.pin2.len = newpinlen;
r = sc_pin_cmd(card, &data, NULL);
if (r) {
if (r == SC_ERROR_PIN_CODE_INCORRECT)
printf("Incorrect code.\n");
printf("Unable to unblock PIN code: %s\n", sc_strerror(r));
return -1;
}
printf("PIN unblocked.\n");
return 0;
}
static int do_get(int argc, char **argv)
{
u8 buf[256];
int r, err = 1;
size_t count = 0;
unsigned int idx = 0;
sc_path_t path;
sc_file_t *file = NULL;
char *filename;
FILE *outf = NULL;
if (argc < 1 || argc > 2)
return usage(do_get);
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_get);
filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_');
outf = (strcmp(filename, "-") == 0)
? stdout
: fopen(filename, "wb");
if (outf == NULL) {
perror(filename);
goto err;
}
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file);
goto err;
}
if (file->type != SC_FILE_TYPE_WORKING_EF) {
printf("only working EFs may be read\n");
goto err;
}
count = file->size;
while (count) {
/* FIXME sc_read_binary does this kind of fetching in a loop already */
int c = count > sizeof(buf) ? sizeof(buf) : count;
r = sc_read_binary(card, idx, buf, c, 0);
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", file);
goto err;
}
if ((r != c) && (card->type != SC_CARD_TYPE_BELPIC_EID)) {
printf("expecting %d, got only %d bytes.\n", c, r);
goto err;
}
if ((r == 0) && (card->type == SC_CARD_TYPE_BELPIC_EID))
break;
fwrite(buf, r, 1, outf);
idx += r;
count -= r;
}
if (outf == stdout) {
fwrite("\n", 1, 1, outf);
}
else {
printf("Total of %d bytes read from %s and saved to %s.\n",
idx, 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[240];
size_t buflen = sizeof(buf);
int r, err = 1;
int offs;
sc_path_t path;
sc_file_t *file;
if (argc != 3)
return usage(do_update_binary);
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_update_binary);
offs = strtol(argv[1],NULL,10);
printf("in: %i; %s\n", offs, argv[2]);
r = parse_string_or_hexdata(argv[2], buf, &buflen);
if (r < 0) {
printf("unable to parse data\n");
return -1;
}
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file);
return -1;
}
if (file->ef_structure != SC_FILE_EF_TRANSPARENT) {
printf("EF structure should be SC_FILE_EF_TRANSPARENT\n");
goto err;
}
r = sc_update_binary(card, offs, buf, buflen, 0);
if (r < 0) {
printf("Cannot update %04X; return %i\n", file->id, r);
goto err;
}
printf("Total of %d bytes written to %04X at %i offset.\n",
r, file->id, offs);
err = 0;
err:
sc_file_free(file);
select_current_path_or_die();
return -err;
}
static int do_update_record(int argc, char **argv)
{
u8 buf[240];
size_t buflen;
int r, i, err = 1;
int rec, offs;
sc_path_t path;
sc_file_t *file;
if (argc != 4)
return usage(do_update_record);
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_update_record);
rec = strtol(argv[1],NULL,10);
offs = strtol(argv[2],NULL,10);
printf("in: %i; %i; %s\n", rec, offs, argv[3]);
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file);
return -1;
}
if (file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE) {
printf("EF structure should be SC_FILE_EF_LINEAR_VARIABLE\n");
goto err;
} else if (rec < 1 || rec > file->record_count) {
printf("Invalid record number %i\n", rec);
goto err;
}
r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR);
if (r<0) {
printf("Cannot read record %i; return %i\n", rec, r);
goto err;;
}
buflen = sizeof(buf) - offs;
i = parse_string_or_hexdata(argv[3], buf + offs, &buflen);
if (!i) {
printf("unable to parse data\n");
goto err;
}
r = sc_update_record(card, rec, buf, r, SC_RECORD_BY_REC_NR);
if (r<0) {
printf("Cannot update record %i; return %i\n", rec, r);
goto err;
}
printf("Total of %d bytes written to record %i at %i offset.\n",
i, rec, offs);
err = 0;
err:
sc_file_free(file);
select_current_path_or_die();
return -err;
}
static int do_put(int argc, char **argv)
{
u8 buf[256];
int r, err = 1;
size_t count = 0;
unsigned int idx = 0;
sc_path_t path;
sc_file_t *file = NULL;
const char *filename;
FILE *outf = NULL;
if (argc < 1 || argc > 2)
return usage(do_put);
if (arg_to_path(argv[0], &path, 0) != 0)
return usage(do_put);
filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_');
outf = fopen(filename, "rb");
if (outf == NULL) {
perror(filename);
goto err;
}
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file);
goto err;
}
count = file->size;
while (count) {
int c = count > sizeof(buf) ? sizeof(buf) : count;
r = fread(buf, 1, c, outf);
if (r < 0) {
perror("fread");
goto err;
}
if (r != c)
count = c = r;
r = sc_update_binary(card, idx, buf, c, 0);
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "update failed", file);
goto err;
}
if (r != c) {
printf("expecting %d, wrote only %d bytes.\n", c, r);
goto err;
}
idx += c;
count -= c;
}
printf("Total of %d bytes written.\n", idx);
err = 0;
err:
sc_file_free(file);
if (outf)
fclose(outf);
select_current_path_or_die();
return -err;
}
static int do_debug(int argc, char **argv)
{
int i;
if (!argc)
printf("Current debug level is %d\n", ctx->debug);
else {
if (sscanf(argv[0], "%d", &i) != 1)
return -1;
printf("Debug level set to %d\n", i);
ctx->debug = i;
if (i > 1) {
sc_ctx_log_to_file(ctx, "stderr");
}
}
return 0;
}
static int do_erase(int argc, char **argv)
{
int r;
if (argc != 0)
return usage(do_erase);
r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL);
if (r) {
printf("Failed to erase card: %s\n", sc_strerror (r));
return -1;
}
return 0;
}
static int do_random(int argc, char **argv)
{
unsigned char buffer[256];
int r, count;
if (argc != 1)
return usage(do_random);
count = atoi(argv[0]);
if (count < 0 || count > 256) {
printf("Number must be in range 0..256\n");
return -1;
}
r = sc_get_challenge(card, buffer, count);
if (r < 0) {
printf("Failed to get random bytes: %s\n", sc_strerror(r));
return -1;
}
util_hex_dump_asc(stdout, buffer, count, 0);
return 0;
}
static int do_get_data(int argc, char **argv)
{
unsigned char buffer[256];
unsigned int tag;
FILE *fp;
int r;
if (argc != 1 && argc != 2)
return usage(do_get_data);
tag = strtoul(argv[0], NULL, 16);
r = sc_get_data(card, tag, buffer, sizeof(buffer));
if (r < 0) {
printf("Failed to get data object: %s\n", sc_strerror(r));
return -1;
}
if (argc == 2) {
const char *filename = argv[1];
if (!(fp = fopen(filename, "w"))) {
perror(filename);
return -1;
}
fwrite(buffer, r, 1, fp);
fclose(fp);
} else {
printf("Object %04x:\n", tag & 0xFFFF);
util_hex_dump_asc(stdout, buffer, r, 0);
}
return 0;
}
/**
* Use PUT DATA command to write to Data Object.
**/
static int do_put_data(int argc, char **argv)
{
unsigned int tag;
u8 buf[8192];
size_t buflen = sizeof(buf);
int r;
if (argc != 2)
return usage(do_put_data);
/* Extract DO's tag */
tag = strtoul(argv[0], NULL, 16);
/* Extract the new content */
/* buflen is the max length of reception buffer */
r = parse_string_or_hexdata(argv[1], buf, &buflen);
if (r < 0) {
printf("unable to parse data\n");
return -1;
}
/* Call OpenSC to do put data */
r = sc_put_data(card, tag, buf, buflen);
if (r < 0) {
printf("Cannot put data to %04X; return %i\n", tag, r);
return -1;
}
printf("Total of %d bytes written.\n", r);
return 0;
}
static int do_apdu(int argc, char **argv)
{
sc_apdu_t apdu;
u8 buf[SC_MAX_APDU_BUFFER_SIZE * 2];
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE * 2];
size_t len, i;
int r;
if (argc < 1)
return usage(do_apdu);
for (i = 0, len = 0; i < (unsigned) argc; i++) {
size_t len0 = strlen(argv[i]);
if ((r = parse_string_or_hexdata(argv[i], buf + len, &len0)) < 0) {
fprintf(stderr, "error parsing %s: %s\n", argv[i], sc_strerror(r));
return r;
};
len += len0;
}
r = sc_bytes2apdu(card->ctx, buf, len, &apdu);
if (r) {
fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(r));
return 2;
}
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
printf("Sending: ");
util_hex_dump(stdout, buf, len, " ");
printf("\n");
r = sc_transmit_apdu(card, &apdu);
if (r) {
fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r));
return 1;
}
printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2,
apdu.resplen ? ":" : "");
if (apdu.resplen)
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r)
printf("Failure: %s\n", sc_strerror(r));
else
printf("Success!\n");
return 0;
}
static int do_asn1(int argc, char **argv)
{
int r, err = 1;
sc_path_t path;
sc_file_t *file = NULL;
int not_current = 1;
size_t len;
unsigned char *buf = NULL;
if (argc > 1)
return usage(do_asn1);
/* select file */
if (argc) {
if (arg_to_path(argv[0], &path, 0) != 0) {
puts("Invalid file path");
return -1;
}
r = sc_select_file(card, &path, &file);
if (r) {
check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file);
goto err;
}
} else {
path = current_path;
file = current_file;
not_current = 0;
}
if (file->type != SC_FILE_TYPE_WORKING_EF) {
printf("only working EFs may be read\n");
goto err;
}
/* read */
if (file->ef_structure != SC_FILE_EF_TRANSPARENT) {
printf("only transparent file type is supported at the moment\n");
goto err;
}
len = file->size;
buf = calloc(1, len);
if (!buf) {
goto err;
}
r = sc_read_binary(card, 0, buf, len, 0);
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", file);
goto err;
}
if ((size_t)r != len) {
printf("WARNING: expecting %lu, got %d bytes.\n", (unsigned long) len, r);
/* some cards return a bogus value for file length. As
* long as the actual length is not higher than the expected
* length, continue */
if(r > (signed)len) {
goto err;
}
}
/* asn1 dump */
sc_asn1_print_tags(buf, r);
err = 0;
err:
if (buf)
free(buf);
if (not_current) {
sc_file_free(file);
select_current_path_or_die();
}
return -err;
}
static int do_sm(int argc, char **argv)
{
int r = SC_ERROR_NOT_SUPPORTED, ret = -1;
if (argc != 1)
return usage(do_sm);
#ifdef ENABLE_SM
if (!strcmp(argv[0],"open")) {
if (!card->sm_ctx.ops.open) {
printf("Not supported\n");
return -1;
}
r = card->sm_ctx.ops.open(card);
}
else if (!strcmp(argv[0],"close")) {
if (!card->sm_ctx.ops.close) {
printf("Not supported\n");
return -1;
}
r = card->sm_ctx.ops.close(card);
}
#endif
if (r == SC_SUCCESS) {
ret = 0;
printf("Success!\n");
}
else {
printf("Failure: %s\n", sc_strerror(r));
}
return ret;
}
static int do_help(int argc, char **argv)
{
struct command *cmd;
if (argc)
return usage(do_help);
printf("Supported commands:\n");
for (cmd = cmds; cmd->name; cmd++) {
int len = strlen(cmd->name) + strlen(cmd->args);
printf(" %s %s%*s %s\n",
cmd->name, cmd->args,
(len > 40) ? 0 : (40 - len), "",
cmd->help);
}
return 0;
}
static int do_quit(int argc, char **argv)
{
die(0);
return 0;
}
static int parse_cmdline(char *in, char **argv, int maxargc)
{
int argc;
for (argc = 0; argc < maxargc; argc++) {
in += strspn(in, " \t\n");
if (*in == '\0')
return argc;
if (*in == '"') {
/* Parse quoted string */
argv[argc] = in++;
in += strcspn(in, "\"");
if (*in++ != '"')
return 0;
} else {
/* White space delimited word */
argv[argc] = in;
in += strcspn(in, " \t\n");
}
if (*in != '\0')
*in++ = '\0';
}
return argc;
}
static char *read_cmdline(FILE *script, char *prompt)
{
static char buf[256];
static int initialized;
static int interactive;
if (!initialized) {
initialized = 1;
interactive = isatty(fileno(script));
#ifdef ENABLE_READLINE
if (interactive)
using_history();
#endif
}
#ifdef ENABLE_READLINE
if (interactive) {
char *line = readline(prompt);
if (line && strlen(line) > 2 )
add_history(line);
return line;
}
#endif
/* Either we don't have readline or we are not running
interactively */
#ifndef ENABLE_READLINE
if (interactive)
printf("%s", prompt);
#endif
fflush(stdout);
if (fgets(buf, sizeof(buf), script) == NULL)
return NULL;
if (strlen(buf) == 0)
return NULL;
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
return buf;
}
int main(int argc, char *argv[])
{
int r, c, long_optind = 0, err = 0;
char *line;
int cargc;
char *cargv[260];
sc_context_param_t ctx_param;
int lcycle = SC_CARDCTRL_LIFECYCLE_ADMIN;
FILE *script = stdin;
printf("OpenSC Explorer version %s\n", sc_get_version());
while (1) {
c = getopt_long(argc, argv, "r:c:vwm:", options, &long_optind);
if (c == -1)
break;
if (c == '?')
util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]");
switch (c) {
case 'r':
opt_reader = optarg;
break;
case 'c':
opt_driver = optarg;
break;
case 'w':
opt_wait = 1;
break;
case 'v':
verbose++;
break;
case 'm':
opt_startfile = optarg;
break;
}
}
memset(&ctx_param, 0, sizeof(ctx_param));
ctx_param.ver = 0;
ctx_param.app_name = app_name;
r = sc_context_create(&ctx, &ctx_param);
if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
return 1;
}
ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
if (verbose > 1) {
ctx->debug = verbose;
ctx->debug_file = stderr;
}
if (opt_driver != NULL) {
err = sc_set_card_driver(ctx, opt_driver);
if (err) {
fprintf(stderr, "Driver '%s' not found!\n", opt_driver);
err = 1;
goto end;
}
}
err = util_connect_card(ctx, &card, opt_reader, opt_wait, 0);
if (err)
goto end;
if (opt_startfile) {
if(*opt_startfile) {
char startpath[1024];
char *args[] = { startpath };
strncpy(startpath, opt_startfile, sizeof(startpath)-1);
r = do_cd(1, args);
if (r) {
printf("unable to select file %s: %s\n",
opt_startfile, sc_strerror(r));
return -1;
}
}
} else {
sc_format_path("3F00", &current_path);
r = sc_select_file(card, &current_path, &current_file);
if (r) {
printf("unable to select MF: %s\n", sc_strerror(r));
return 1;
}
}
r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle);
if (r && r != SC_ERROR_NOT_SUPPORTED)
printf("unable to change lifecycle: %s\n", sc_strerror(r));
switch (argc - optind) {
default:
util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]");
break;
case 0:
script = stdin;
break;
case 1:
if (strcmp(argv[optind], "-") == 0) {
script = stdin;
}
else if ((script = fopen(argv[optind], "r")) == NULL) {
util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]");
}
break;
}
while (!feof(script)) {
struct command *cmd;
char prompt[3*SC_MAX_PATH_STRING_SIZE];
sprintf(prompt, "OpenSC [%s]> ", path_to_filename(&current_path, '/'));
line = read_cmdline(script, prompt);
if (line == NULL)
break;
cargc = parse_cmdline(line, cargv, DIM(cargv));
if ((cargc < 1) || (*cargv[0] == '#'))
continue;
for (r=cargc; r < (int)DIM(cargv); r++)
cargv[r] = "";
cmd = ambiguous_match(cmds, cargv[0]);
if (cmd == NULL) {
do_help(0, NULL);
} else {
cmd->func(cargc-1, cargv+1);
}
}
end:
die(err);
return 0; /* not reached */
}