2001-11-22 15:40:51 +00:00
|
|
|
/*
|
2005-06-16 19:35:31 +00:00
|
|
|
* opensc-tool.c: Tool for accessing smart cards with libopensc
|
2001-11-22 15:40:51 +00:00
|
|
|
*
|
2006-12-19 21:35:42 +00:00
|
|
|
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
2001-11-22 15:40:51 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2001-11-18 01:52:32 +00:00
|
|
|
|
2002-02-25 16:30:38 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
2001-12-30 21:17:34 +00:00
|
|
|
#include <stdio.h>
|
2001-11-18 01:52:32 +00:00
|
|
|
#include <stdlib.h>
|
2002-10-19 14:04:52 +00:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2001-12-30 21:17:34 +00:00
|
|
|
#include <unistd.h>
|
2002-06-14 12:52:56 +00:00
|
|
|
#endif
|
2001-11-18 01:52:32 +00:00
|
|
|
#include <string.h>
|
2002-04-05 13:48:00 +00:00
|
|
|
#include <errno.h>
|
2001-12-25 20:45:48 +00:00
|
|
|
#include <ctype.h>
|
2002-04-05 13:48:00 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <opensc/opensc.h>
|
2004-12-16 08:50:51 +00:00
|
|
|
#include <opensc/cardctl.h>
|
2002-04-05 13:48:00 +00:00
|
|
|
#include "util.h"
|
2001-11-18 01:52:32 +00:00
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const char *app_name = "opensc-tool";
|
2002-03-24 14:12:38 +00:00
|
|
|
|
2003-10-14 08:10:39 +00:00
|
|
|
static int opt_reader = -1,
|
|
|
|
opt_wait = 0;
|
|
|
|
static char ** opt_apdus;
|
|
|
|
static int opt_apdu_count = 0;
|
2004-06-13 20:13:12 +00:00
|
|
|
static int verbose = 0;
|
2001-11-18 01:52:32 +00:00
|
|
|
|
2004-12-16 08:50:51 +00:00
|
|
|
enum {
|
|
|
|
OPT_SERIAL = 0x100,
|
|
|
|
};
|
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const struct option options[] = {
|
2008-03-06 16:06:59 +00:00
|
|
|
{ "info", 0, NULL, 'i' },
|
2007-06-21 12:01:39 +00:00
|
|
|
{ "atr", 0, NULL, 'a' },
|
|
|
|
{ "serial", 0, NULL, OPT_SERIAL },
|
|
|
|
{ "name", 0, NULL, 'n' },
|
2008-04-12 21:54:02 +00:00
|
|
|
{ "get-conf-entry", 1, NULL, 'G' },
|
|
|
|
{ "set-conf-entry", 1, NULL, 'S' },
|
2007-06-21 12:01:39 +00:00
|
|
|
{ "list-readers", 0, NULL, 'l' },
|
|
|
|
{ "list-drivers", 0, NULL, 'D' },
|
|
|
|
{ "list-rdrivers", 0, NULL, 'R' },
|
|
|
|
{ "list-files", 0, NULL, 'f' },
|
|
|
|
{ "send-apdu", 1, NULL, 's' },
|
|
|
|
{ "reader", 1, NULL, 'r' },
|
|
|
|
{ "card-driver", 1, NULL, 'c' },
|
|
|
|
{ "wait", 0, NULL, 'w' },
|
|
|
|
{ "verbose", 0, NULL, 'v' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
2001-11-18 01:52:32 +00:00
|
|
|
};
|
|
|
|
|
2007-06-29 13:31:04 +00:00
|
|
|
static const char *option_help[] = {
|
2008-03-06 16:06:59 +00:00
|
|
|
"Prints information about OpenSC",
|
2002-01-20 21:20:09 +00:00
|
|
|
"Prints the ATR bytes of the card",
|
2004-12-16 08:50:51 +00:00
|
|
|
"Prints the card serial number",
|
2003-05-30 08:33:19 +00:00
|
|
|
"Identify the card and print its name",
|
2008-04-12 21:54:02 +00:00
|
|
|
"Get configuration key, format: section:name:key",
|
|
|
|
"Set configuration key, format: section:name:key:value",
|
2001-11-18 01:52:32 +00:00
|
|
|
"Lists all configured readers",
|
2001-12-25 20:45:48 +00:00
|
|
|
"Lists all installed card drivers",
|
2002-11-28 15:43:54 +00:00
|
|
|
"Lists all installed reader drivers",
|
2001-11-18 01:52:32 +00:00
|
|
|
"Recursively lists files stored on card",
|
2001-11-20 22:21:58 +00:00
|
|
|
"Sends an APDU in format AA:BB:CC:DD:EE:FF...",
|
2002-01-08 13:56:50 +00:00
|
|
|
"Uses reader number <arg> [0]",
|
|
|
|
"Forces the use of driver <arg> [auto-detect]",
|
2003-01-03 16:58:32 +00:00
|
|
|
"Wait for a card to be inserted",
|
2004-06-13 20:13:12 +00:00
|
|
|
"Verbose operation. Use several times to enable debug output.",
|
2001-11-18 01:52:32 +00:00
|
|
|
};
|
|
|
|
|
2007-06-21 12:01:39 +00:00
|
|
|
static sc_context_t *ctx = NULL;
|
|
|
|
static sc_card_t *card = NULL;
|
2001-11-18 01:52:32 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
static int opensc_info(void)
|
|
|
|
{
|
|
|
|
printf (
|
|
|
|
"%s %s ",
|
|
|
|
PACKAGE_NAME,
|
|
|
|
PACKAGE_VERSION
|
|
|
|
);
|
|
|
|
|
|
|
|
#if defined(__VERSION__)
|
|
|
|
printf (
|
|
|
|
"[%s %s]\n",
|
|
|
|
#if defined(__GNUC__)
|
|
|
|
"gcc ",
|
|
|
|
#else
|
|
|
|
"unknown ",
|
|
|
|
#endif
|
|
|
|
__VERSION__
|
|
|
|
);
|
2009-12-03 07:03:53 +00:00
|
|
|
#elif defined(__SUNPRO_C)
|
|
|
|
printf (
|
|
|
|
"[Sun C %x.%x]\n",
|
|
|
|
#if __SUNPRO_C > 0x590
|
2009-12-03 11:13:17 +00:00
|
|
|
(__SUNPRO_C >> 12), (__SUNPRO_C >> 4) & 0xFF
|
2009-12-03 07:03:53 +00:00
|
|
|
#else
|
|
|
|
(__SUNPRO_C >> 8), (__SUNPRO_C >> 4) & 0xF
|
|
|
|
#endif
|
|
|
|
);
|
2008-03-06 16:06:59 +00:00
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
printf ("[Microsoft %d]\n", _MSC_VER);
|
|
|
|
#else
|
|
|
|
printf ("[Unknown compiler, please report]");
|
|
|
|
#endif
|
|
|
|
printf ("Enabled features:%s\n", OPENSC_FEATURES);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-12 21:54:02 +00:00
|
|
|
static int opensc_get_conf_entry(const char *config)
|
|
|
|
{
|
|
|
|
scconf_block *conf_block = NULL, **blocks;
|
|
|
|
char *buffer = NULL;
|
|
|
|
char *section = NULL;
|
|
|
|
char *name = NULL;
|
|
|
|
char *key = NULL;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
if (ctx->conf == NULL) {
|
|
|
|
r = ENOENT;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buffer = strdup(config)) == NULL) {
|
|
|
|
r = ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
section = buffer;
|
|
|
|
name = section == NULL ? NULL : strchr(section+1, ':');
|
|
|
|
key = name == NULL ? NULL : strchr(name+1, ':');
|
|
|
|
if (key == NULL) {
|
|
|
|
r = EINVAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*name = '\0';
|
|
|
|
name++;
|
|
|
|
*key = '\0';
|
|
|
|
key++;
|
|
|
|
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, NULL, section, name);
|
|
|
|
if (blocks[0])
|
|
|
|
conf_block = blocks[0];
|
|
|
|
free(blocks);
|
|
|
|
if (conf_block != NULL) {
|
|
|
|
const char *value = scconf_get_str(conf_block, key, NULL);
|
|
|
|
|
|
|
|
if (value != NULL) {
|
|
|
|
printf ("%s\n", value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
if (buffer != NULL)
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int opensc_set_conf_entry(const char *config)
|
|
|
|
{
|
|
|
|
scconf_block *conf_block = NULL, **blocks;
|
|
|
|
char *buffer = NULL;
|
|
|
|
char *section = NULL;
|
|
|
|
char *name = NULL;
|
|
|
|
char *key = NULL;
|
|
|
|
char *value = NULL;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
if (ctx->conf == NULL) {
|
|
|
|
r = ENOENT;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buffer = strdup(config)) == NULL) {
|
|
|
|
r = ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
section = buffer;
|
|
|
|
name = section == NULL ? NULL : strchr(section+1, ':');
|
|
|
|
key = name == NULL ? NULL : strchr(name+1, ':');
|
|
|
|
value = key == NULL ? NULL : strchr(key+1, ':');
|
|
|
|
if (value == NULL) {
|
|
|
|
r = EINVAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*name = '\0';
|
|
|
|
name++;
|
|
|
|
*key = '\0';
|
|
|
|
key++;
|
|
|
|
*value = '\0';
|
|
|
|
value++;
|
|
|
|
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, NULL, section, name);
|
|
|
|
if (blocks[0])
|
|
|
|
conf_block = blocks[0];
|
|
|
|
free(blocks);
|
|
|
|
if (conf_block != NULL) {
|
|
|
|
scconf_item *item;
|
|
|
|
|
|
|
|
for (item = conf_block->items; item != NULL; item = item->next) {
|
|
|
|
scconf_list *list;
|
|
|
|
|
|
|
|
if ((item->type != SCCONF_ITEM_TYPE_VALUE)
|
|
|
|
|| (strcmp(item->key, key) != 0))
|
|
|
|
continue;
|
|
|
|
list = item->value.list;
|
|
|
|
scconf_list_destroy(list);
|
|
|
|
list = NULL;
|
|
|
|
scconf_list_add(&list, value);
|
|
|
|
item->value.list = list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (item == NULL)
|
|
|
|
scconf_put_str(conf_block, key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write */
|
|
|
|
if ((r = scconf_write(ctx->conf, ctx->conf->filename)) != 0) {
|
|
|
|
fprintf(stderr, "scconf_write(): %s\n", strerror(r));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
if (buffer != NULL)
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
static int list_readers(void)
|
2001-11-18 01:52:32 +00:00
|
|
|
{
|
2006-02-15 17:29:40 +00:00
|
|
|
unsigned int i, rcount = sc_ctx_get_reader_count(ctx);
|
2001-11-18 01:52:32 +00:00
|
|
|
|
2006-02-15 17:29:40 +00:00
|
|
|
if (rcount == 0) {
|
2008-12-06 11:41:00 +00:00
|
|
|
printf("No smart card readers found.\n");
|
2001-12-25 20:45:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-09-14 09:48:56 +00:00
|
|
|
printf("Nr. Driver Features Name\n");
|
2006-02-15 17:29:40 +00:00
|
|
|
for (i = 0; i < rcount; i++) {
|
|
|
|
sc_reader_t *screader = sc_ctx_get_reader(ctx, i);
|
2009-09-14 09:48:56 +00:00
|
|
|
printf("%-7d%-11s%-10s%s\n", i, screader->driver->short_name,
|
|
|
|
screader->slot[0].capabilities & SC_SLOT_CAP_PIN_PAD ? "PINpad":"",
|
2006-02-15 17:29:40 +00:00
|
|
|
screader->name);
|
2001-11-18 01:52:32 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
static int list_reader_drivers(void)
|
2002-03-01 11:52:55 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ctx->reader_drivers[0] == NULL) {
|
|
|
|
printf("No reader drivers installed!\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
printf("Configured reader drivers:\n");
|
|
|
|
for (i = 0; ctx->reader_drivers[i] != NULL; i++) {
|
|
|
|
printf(" %-16s %s\n", ctx->reader_drivers[i]->short_name,
|
|
|
|
ctx->reader_drivers[i]->name);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
static int list_drivers(void)
|
2001-12-25 20:45:48 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ctx->card_drivers[0] == NULL) {
|
|
|
|
printf("No card drivers installed!\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
printf("Configured card drivers:\n");
|
|
|
|
for (i = 0; ctx->card_drivers[i] != NULL; i++) {
|
2002-01-08 13:56:50 +00:00
|
|
|
printf(" %-16s %s\n", ctx->card_drivers[i]->short_name,
|
|
|
|
ctx->card_drivers[i]->name);
|
2001-11-18 01:52:32 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-03-09 00:04:44 +00:00
|
|
|
static int print_file(sc_card_t *in_card, const sc_file_t *file,
|
|
|
|
const sc_path_t *path, int depth)
|
2001-11-18 01:52:32 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
const char *tmps;
|
2001-12-29 02:07:32 +00:00
|
|
|
const char *ac_ops_df[] = {
|
2002-01-08 13:56:50 +00:00
|
|
|
"select", "lock", "delete", "create", "rehab", "inval",
|
|
|
|
"list"
|
2001-12-29 02:07:32 +00:00
|
|
|
};
|
|
|
|
const char *ac_ops_ef[] = {
|
2009-06-16 09:17:53 +00:00
|
|
|
"read", "update", "erase", "write", "rehab", "inval"
|
2001-12-29 02:07:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
for (r = 0; r < depth; r++)
|
2001-11-18 01:52:32 +00:00
|
|
|
printf(" ");
|
2005-11-29 20:56:11 +00:00
|
|
|
printf("%s ", sc_print_path(path));
|
2001-12-29 02:07:32 +00:00
|
|
|
if (file->namelen) {
|
|
|
|
printf("[");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_print_binary(stdout, file->name, file->namelen);
|
2001-12-29 02:07:32 +00:00
|
|
|
printf("] ");
|
|
|
|
}
|
|
|
|
switch (file->type) {
|
|
|
|
case SC_FILE_TYPE_WORKING_EF:
|
|
|
|
tmps = "wEF";
|
|
|
|
break;
|
|
|
|
case SC_FILE_TYPE_INTERNAL_EF:
|
|
|
|
tmps = "iEF";
|
|
|
|
break;
|
|
|
|
case SC_FILE_TYPE_DF:
|
|
|
|
tmps = " DF";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tmps = "unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
printf("type: %-3s, ", tmps);
|
|
|
|
if (file->type != SC_FILE_TYPE_DF) {
|
|
|
|
const char *structs[] = {
|
|
|
|
"unknown", "transpnt", "linrfix", "linrfix(TLV)",
|
|
|
|
"linvar", "linvar(TLV)", "lincyc", "lincyc(TLV)"
|
|
|
|
};
|
2006-09-26 18:01:03 +00:00
|
|
|
int ef_type = file->ef_structure;
|
|
|
|
if (ef_type < 0 || ef_type > 7)
|
|
|
|
ef_type = 0; /* invalid or unknow ef type */
|
|
|
|
printf("ef structure: %s, ", structs[ef_type]);
|
2001-12-29 02:07:32 +00:00
|
|
|
}
|
2006-05-01 10:02:50 +00:00
|
|
|
printf("size: %lu\n", (unsigned long) file->size);
|
2001-12-29 02:07:32 +00:00
|
|
|
for (r = 0; r < depth; r++)
|
|
|
|
printf(" ");
|
|
|
|
if (file->type == SC_FILE_TYPE_DF)
|
2005-02-05 10:02:56 +00:00
|
|
|
for (r = 0; r < (int) (sizeof(ac_ops_df)/sizeof(ac_ops_df[0])); r++)
|
2008-03-06 16:06:59 +00:00
|
|
|
printf("%s[%s] ", ac_ops_df[r], util_acl_to_str(sc_file_get_acl_entry(file, r)));
|
2001-12-29 02:07:32 +00:00
|
|
|
else
|
2005-02-05 10:02:56 +00:00
|
|
|
for (r = 0; r < (int) (sizeof(ac_ops_ef)/sizeof(ac_ops_ef[0])); r++)
|
2008-03-06 16:06:59 +00:00
|
|
|
printf("%s[%s] ", ac_ops_ef[r], util_acl_to_str(sc_file_get_acl_entry(file, r)));
|
2001-12-29 02:07:32 +00:00
|
|
|
|
|
|
|
if (file->sec_attr_len) {
|
|
|
|
printf("sec: ");
|
|
|
|
/* Octets are as follows:
|
|
|
|
* DF: select, lock, delete, create, rehab, inval
|
|
|
|
* EF: read, update, write, erase, rehab, inval
|
|
|
|
* 4 MSB's of the octet mean:
|
|
|
|
* 0 = ALW, 1 = PIN1, 2 = PIN2, 4 = SYS,
|
|
|
|
* 15 = NEV */
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, ":");
|
2001-12-29 02:07:32 +00:00
|
|
|
}
|
|
|
|
if (file->prop_attr_len) {
|
2001-12-20 12:16:05 +00:00
|
|
|
printf("\n");
|
2001-12-29 02:07:32 +00:00
|
|
|
for (r = 0; r < depth; r++)
|
|
|
|
printf(" ");
|
|
|
|
printf("prop: ");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, ":");
|
2001-12-29 02:07:32 +00:00
|
|
|
}
|
|
|
|
printf("\n\n");
|
2003-04-03 09:52:41 +00:00
|
|
|
|
|
|
|
if (file->type == SC_FILE_TYPE_DF)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
|
|
|
|
unsigned char *buf;
|
|
|
|
|
2003-09-10 22:20:26 +00:00
|
|
|
if (!(buf = (unsigned char *) malloc(file->size))) {
|
2003-03-28 13:28:44 +00:00
|
|
|
fprintf(stderr, "out of memory");
|
|
|
|
return 1;
|
|
|
|
}
|
2003-04-03 09:52:41 +00:00
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
r = sc_read_binary(in_card, 0, buf, file->size, 0);
|
2003-04-03 09:52:41 +00:00
|
|
|
if (r > 0)
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, buf, r, 0);
|
2003-04-03 09:52:41 +00:00
|
|
|
free(buf);
|
|
|
|
} else {
|
|
|
|
unsigned char buf[256];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; i < file->record_count; i++) {
|
|
|
|
printf("Record %d\n", i);
|
2005-02-05 10:02:56 +00:00
|
|
|
r = sc_read_record(in_card, i, buf, 256, 0);
|
2001-12-27 17:25:10 +00:00
|
|
|
if (r > 0)
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, buf, r, 0);
|
2001-12-27 17:25:10 +00:00
|
|
|
}
|
2001-11-18 01:52:32 +00:00
|
|
|
}
|
2001-12-29 02:07:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-03-09 00:04:44 +00:00
|
|
|
static int enum_dir(sc_path_t path, int depth)
|
2001-12-29 02:07:32 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_file_t *file;
|
2002-02-20 09:56:47 +00:00
|
|
|
int r, file_type;
|
2002-02-24 19:32:14 +00:00
|
|
|
u8 files[SC_MAX_APDU_BUFFER_SIZE];
|
2001-12-29 02:07:32 +00:00
|
|
|
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2002-02-20 09:56:47 +00:00
|
|
|
print_file(card, file, &path, depth);
|
|
|
|
file_type = file->type;
|
|
|
|
sc_file_free(file);
|
2002-08-20 08:39:11 +00:00
|
|
|
if (file_type == SC_FILE_TYPE_DF) {
|
2001-11-18 01:52:32 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
r = sc_list_files(card, files, sizeof(files));
|
2003-10-14 10:10:24 +00:00
|
|
|
if (r < 0) {
|
2001-11-18 01:52:32 +00:00
|
|
|
fprintf(stderr, "sc_list_files() failed: %s\n", sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2003-10-14 10:10:24 +00:00
|
|
|
if (r == 0) {
|
|
|
|
printf("Empty directory\n");
|
|
|
|
} else
|
2001-11-18 01:52:32 +00:00
|
|
|
for (i = 0; i < r/2; i++) {
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t tmppath;
|
2001-11-18 01:52:32 +00:00
|
|
|
|
|
|
|
memcpy(&tmppath, &path, sizeof(path));
|
|
|
|
memcpy(tmppath.value + tmppath.len, files + 2*i, 2);
|
|
|
|
tmppath.len += 2;
|
|
|
|
enum_dir(tmppath, depth + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
static int list_files(void)
|
2001-11-18 01:52:32 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
2001-11-18 01:52:32 +00:00
|
|
|
int r;
|
|
|
|
|
2001-12-22 20:52:57 +00:00
|
|
|
sc_format_path("3F00", &path);
|
2001-11-18 01:52:32 +00:00
|
|
|
r = enum_dir(path, 0);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
static int send_apdu(void)
|
2001-11-20 22:21:58 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_apdu_t apdu;
|
2002-02-24 19:32:14 +00:00
|
|
|
u8 buf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE],
|
|
|
|
rbuf[SC_MAX_APDU_BUFFER_SIZE], *p;
|
2002-01-08 13:56:50 +00:00
|
|
|
size_t len, len0, r;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
for (c = 0; c < opt_apdu_count; c++) {
|
|
|
|
len0 = sizeof(buf);
|
|
|
|
sc_hex_to_bin(opt_apdus[c], buf, &len0);
|
|
|
|
if (len0 < 4) {
|
|
|
|
fprintf(stderr, "APDU too short (must be at least 4 bytes).\n");
|
2001-12-22 20:52:57 +00:00
|
|
|
return 2;
|
|
|
|
}
|
2002-01-08 13:56:50 +00:00
|
|
|
len = len0;
|
|
|
|
p = buf;
|
2003-03-25 11:19:49 +00:00
|
|
|
memset(&apdu, 0, sizeof(apdu));
|
2002-01-08 13:56:50 +00:00
|
|
|
apdu.cla = *p++;
|
|
|
|
apdu.ins = *p++;
|
|
|
|
apdu.p1 = *p++;
|
|
|
|
apdu.p2 = *p++;
|
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
len -= 4;
|
|
|
|
if (len > 1) {
|
|
|
|
apdu.lc = *p++;
|
|
|
|
len--;
|
|
|
|
memcpy(sbuf, p, apdu.lc);
|
|
|
|
apdu.data = sbuf;
|
|
|
|
apdu.datalen = apdu.lc;
|
|
|
|
if (len < apdu.lc) {
|
2006-04-26 11:41:57 +00:00
|
|
|
fprintf(stderr, "APDU too short (need %lu bytes).\n",
|
2006-05-01 10:02:50 +00:00
|
|
|
(unsigned long) apdu.lc-len);
|
2002-01-08 13:56:50 +00:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
len -= apdu.lc;
|
2007-01-15 20:32:58 +00:00
|
|
|
p += apdu.lc;
|
2002-01-08 13:56:50 +00:00
|
|
|
if (len) {
|
|
|
|
apdu.le = *p++;
|
2002-02-11 15:55:34 +00:00
|
|
|
if (apdu.le == 0)
|
|
|
|
apdu.le = 256;
|
2002-01-08 13:56:50 +00:00
|
|
|
len--;
|
|
|
|
apdu.cse = SC_APDU_CASE_4_SHORT;
|
|
|
|
} else
|
|
|
|
apdu.cse = SC_APDU_CASE_3_SHORT;
|
|
|
|
if (len) {
|
2006-05-01 10:02:50 +00:00
|
|
|
fprintf(stderr, "APDU too long (%lu bytes extra).\n",
|
|
|
|
(unsigned long) len);
|
2002-01-08 13:56:50 +00:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
} else if (len == 1) {
|
2001-11-20 22:21:58 +00:00
|
|
|
apdu.le = *p++;
|
2002-02-11 15:55:34 +00:00
|
|
|
if (apdu.le == 0)
|
|
|
|
apdu.le = 256;
|
2001-11-20 22:21:58 +00:00
|
|
|
len--;
|
2002-01-08 13:56:50 +00:00
|
|
|
apdu.cse = SC_APDU_CASE_2_SHORT;
|
2001-11-20 22:21:58 +00:00
|
|
|
} else
|
2002-01-08 13:56:50 +00:00
|
|
|
apdu.cse = SC_APDU_CASE_1;
|
|
|
|
printf("Sending: ");
|
|
|
|
for (r = 0; r < len0; r++)
|
|
|
|
printf("%02X ", buf[r]);
|
|
|
|
printf("\n");
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r));
|
|
|
|
return 1;
|
2001-11-20 22:21:58 +00:00
|
|
|
}
|
2002-01-08 13:56:50 +00:00
|
|
|
printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2,
|
|
|
|
apdu.resplen ? ":" : "");
|
|
|
|
if (apdu.resplen)
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
2001-11-20 22:21:58 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
static void print_serial(sc_card_t *in_card)
|
2004-12-16 08:50:51 +00:00
|
|
|
{
|
|
|
|
int r;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_serial_number_t serial;
|
2004-12-16 08:50:51 +00:00
|
|
|
|
2005-02-05 10:02:56 +00:00
|
|
|
r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial);
|
2004-12-16 08:50:51 +00:00
|
|
|
if (r)
|
|
|
|
fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed\n");
|
2004-12-20 08:03:40 +00:00
|
|
|
else
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, serial.value, serial.len, -1);
|
2004-12-16 08:50:51 +00:00
|
|
|
}
|
|
|
|
|
2001-11-18 01:52:32 +00:00
|
|
|
int main(int argc, char * const argv[])
|
|
|
|
{
|
|
|
|
int err = 0, r, c, long_optind = 0;
|
2008-03-06 16:06:59 +00:00
|
|
|
int do_info = 0;
|
2008-04-12 21:54:02 +00:00
|
|
|
int do_get_conf_entry = 0;
|
|
|
|
int do_set_conf_entry = 0;
|
2001-11-18 01:52:32 +00:00
|
|
|
int do_list_readers = 0;
|
2001-12-25 20:45:48 +00:00
|
|
|
int do_list_drivers = 0;
|
2002-03-01 11:52:55 +00:00
|
|
|
int do_list_rdrivers = 0;
|
2001-11-18 01:52:32 +00:00
|
|
|
int do_list_files = 0;
|
2001-11-20 22:21:58 +00:00
|
|
|
int do_send_apdu = 0;
|
2002-01-20 21:20:09 +00:00
|
|
|
int do_print_atr = 0;
|
2004-12-16 08:50:51 +00:00
|
|
|
int do_print_serial = 0;
|
2003-05-30 08:33:19 +00:00
|
|
|
int do_print_name = 0;
|
2001-11-18 01:52:32 +00:00
|
|
|
int action_count = 0;
|
2002-01-08 13:56:50 +00:00
|
|
|
const char *opt_driver = NULL;
|
2008-04-12 21:54:02 +00:00
|
|
|
const char *opt_conf_entry = NULL;
|
2006-02-07 20:14:43 +00:00
|
|
|
sc_context_param_t ctx_param;
|
2001-11-18 01:52:32 +00:00
|
|
|
|
2003-10-14 11:23:18 +00:00
|
|
|
setbuf(stderr, NULL);
|
|
|
|
setbuf(stdout, NULL);
|
2003-10-14 08:10:39 +00:00
|
|
|
|
2001-11-18 01:52:32 +00:00
|
|
|
while (1) {
|
2008-04-12 21:54:02 +00:00
|
|
|
c = getopt_long(argc, argv, "inlG:S:fr:vs:DRc:aw", options, &long_optind);
|
2001-11-18 01:52:32 +00:00
|
|
|
if (c == -1)
|
|
|
|
break;
|
|
|
|
if (c == '?')
|
2008-03-06 16:06:59 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help);
|
2001-11-18 01:52:32 +00:00
|
|
|
switch (c) {
|
2008-03-06 16:06:59 +00:00
|
|
|
case 'i':
|
|
|
|
do_info = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2008-04-12 21:54:02 +00:00
|
|
|
case 'G':
|
|
|
|
do_get_conf_entry = 1;
|
|
|
|
opt_conf_entry = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
do_set_conf_entry = 1;
|
|
|
|
opt_conf_entry = optarg;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2001-11-18 01:52:32 +00:00
|
|
|
case 'l':
|
|
|
|
do_list_readers = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2001-12-25 20:45:48 +00:00
|
|
|
case 'D':
|
|
|
|
do_list_drivers = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2002-03-01 11:52:55 +00:00
|
|
|
case 'R':
|
|
|
|
do_list_rdrivers = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2001-11-18 01:52:32 +00:00
|
|
|
case 'f':
|
|
|
|
do_list_files = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2001-11-20 22:21:58 +00:00
|
|
|
case 's':
|
2003-10-14 08:10:39 +00:00
|
|
|
opt_apdus = (char **) realloc(opt_apdus,
|
|
|
|
(opt_apdu_count + 1) * sizeof(char *));
|
2002-01-08 13:56:50 +00:00
|
|
|
opt_apdus[opt_apdu_count] = optarg;
|
2001-11-20 22:21:58 +00:00
|
|
|
do_send_apdu++;
|
2002-01-08 13:56:50 +00:00
|
|
|
if (opt_apdu_count == 0)
|
|
|
|
action_count++;
|
|
|
|
opt_apdu_count++;
|
2001-11-20 22:21:58 +00:00
|
|
|
break;
|
2002-01-20 21:20:09 +00:00
|
|
|
case 'a':
|
|
|
|
do_print_atr = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2003-05-30 08:33:19 +00:00
|
|
|
case 'n':
|
|
|
|
do_print_name = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2002-01-08 13:56:50 +00:00
|
|
|
case 'r':
|
2001-11-18 20:36:15 +00:00
|
|
|
opt_reader = atoi(optarg);
|
|
|
|
break;
|
2004-06-13 20:13:12 +00:00
|
|
|
case 'v':
|
|
|
|
verbose++;
|
2001-11-20 22:21:58 +00:00
|
|
|
break;
|
2002-01-08 13:56:50 +00:00
|
|
|
case 'c':
|
|
|
|
opt_driver = optarg;
|
2001-12-20 12:16:05 +00:00
|
|
|
break;
|
2003-01-03 16:58:32 +00:00
|
|
|
case 'w':
|
|
|
|
opt_wait = 1;
|
|
|
|
break;
|
2004-12-16 08:50:51 +00:00
|
|
|
case OPT_SERIAL:
|
|
|
|
do_print_serial = 1;
|
|
|
|
action_count++;
|
|
|
|
break;
|
2001-11-18 01:52:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (action_count == 0)
|
2008-03-06 16:06:59 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help);
|
|
|
|
|
|
|
|
if (do_info) {
|
|
|
|
opensc_info();
|
|
|
|
action_count--;
|
|
|
|
}
|
2006-02-07 20:14:43 +00:00
|
|
|
|
|
|
|
memset(&ctx_param, 0, sizeof(ctx_param));
|
|
|
|
ctx_param.ver = 0;
|
|
|
|
ctx_param.app_name = app_name;
|
|
|
|
|
|
|
|
r = sc_context_create(&ctx, &ctx_param);
|
2001-11-18 01:52:32 +00:00
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
|
|
|
|
return 1;
|
|
|
|
}
|
2004-06-13 20:13:12 +00:00
|
|
|
if (verbose > 1)
|
|
|
|
ctx->debug = verbose-1;
|
2008-04-12 21:54:02 +00:00
|
|
|
if (do_get_conf_entry) {
|
|
|
|
if ((err = opensc_get_conf_entry (opt_conf_entry)))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
if (do_set_conf_entry) {
|
|
|
|
if ((err = opensc_set_conf_entry (opt_conf_entry)))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
2002-03-01 11:52:55 +00:00
|
|
|
if (do_list_rdrivers) {
|
|
|
|
if ((err = list_reader_drivers()))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
2001-11-18 01:52:32 +00:00
|
|
|
if (do_list_readers) {
|
|
|
|
if ((err = list_readers()))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
2001-12-25 20:45:48 +00:00
|
|
|
if (do_list_drivers) {
|
|
|
|
if ((err = list_drivers()))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
2001-11-18 01:52:32 +00:00
|
|
|
if (action_count <= 0)
|
|
|
|
goto end;
|
2003-01-03 16:58:32 +00:00
|
|
|
|
2002-01-08 13:56:50 +00:00
|
|
|
if (opt_driver != NULL) {
|
2002-01-10 12:33:56 +00:00
|
|
|
err = sc_set_card_driver(ctx, opt_driver);
|
2002-01-08 13:56:50 +00:00
|
|
|
if (err) {
|
|
|
|
fprintf(stderr, "Driver '%s' not found!\n", opt_driver);
|
|
|
|
err = 1;
|
|
|
|
goto end;
|
|
|
|
}
|
2001-11-18 01:52:32 +00:00
|
|
|
}
|
2003-01-03 16:58:32 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
err = util_connect_card(ctx, &card, opt_reader, 0, opt_wait, verbose);
|
2003-01-03 16:58:32 +00:00
|
|
|
if (err)
|
2001-11-18 01:52:32 +00:00
|
|
|
goto end;
|
2003-01-03 16:58:32 +00:00
|
|
|
|
2002-01-20 21:20:09 +00:00
|
|
|
if (do_print_atr) {
|
2005-01-19 20:39:25 +00:00
|
|
|
if (verbose) {
|
|
|
|
printf("Card ATR:\n");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, card->atr, card->atr_len, -1);
|
2005-01-19 20:39:25 +00:00
|
|
|
} else {
|
|
|
|
char tmp[SC_MAX_ATR_SIZE*3];
|
|
|
|
sc_bin_to_hex(card->atr, card->atr_len, tmp, sizeof(tmp) - 1, ':');
|
|
|
|
fprintf(stdout,"%s\n",tmp);
|
|
|
|
}
|
2002-01-20 21:20:09 +00:00
|
|
|
action_count--;
|
|
|
|
}
|
2004-12-16 08:50:51 +00:00
|
|
|
if (do_print_serial) {
|
|
|
|
if (verbose)
|
|
|
|
printf("Card serial number:");
|
|
|
|
print_serial(card);
|
|
|
|
action_count--;
|
|
|
|
}
|
2003-05-30 08:33:19 +00:00
|
|
|
if (do_print_name) {
|
2004-06-13 20:13:12 +00:00
|
|
|
if (verbose)
|
2003-05-30 08:33:19 +00:00
|
|
|
printf("Card name: ");
|
|
|
|
printf("%s\n", card->name);
|
|
|
|
action_count--;
|
|
|
|
}
|
2001-11-20 22:21:58 +00:00
|
|
|
if (do_send_apdu) {
|
|
|
|
if ((err = send_apdu()))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
|
2001-11-18 01:52:32 +00:00
|
|
|
if (do_list_files) {
|
|
|
|
if ((err = list_files()))
|
|
|
|
goto end;
|
|
|
|
action_count--;
|
|
|
|
}
|
|
|
|
end:
|
|
|
|
if (card) {
|
|
|
|
sc_unlock(card);
|
2002-02-24 19:32:14 +00:00
|
|
|
sc_disconnect_card(card, 0);
|
2001-11-18 01:52:32 +00:00
|
|
|
}
|
|
|
|
if (ctx)
|
2002-03-24 14:12:38 +00:00
|
|
|
sc_release_context(ctx);
|
2001-11-18 01:52:32 +00:00
|
|
|
return err;
|
|
|
|
}
|