opensc/src/tools/opensc-tool.c

454 lines
10 KiB
C
Raw Normal View History

/*
* opensc-tool.c: Tool for accessing SmartCards with libopensc
*
* Copyright (C) 2001 Juha Yrj<EFBFBD>l<EFBFBD> <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 "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <opensc.h>
#include <sys/stat.h>
#include <ctype.h>
#define OPT_CHANGE_PIN 0x100
#define OPT_LIST_PINS 0x101
#define OPT_READER 0x102
#define OPT_PIN_ID 0x103
#define OPT_NO_CACHE 0x104
int opt_reader = 0, opt_no_cache = 0, opt_debug = 0;
char * opt_apdus[8];
int opt_apdu_count = 0;
int quiet = 0;
const struct option options[] = {
{ "atr", 0, 0, 'a' },
{ "list-readers", 0, 0, 'l' },
{ "list-drivers", 0, 0, 'D' },
{ "list-files", 0, 0, 'f' },
{ "send-apdu", 1, 0, 's' },
{ "reader", 1, 0, 'r' },
{ "card-driver", 1, 0, 'c' },
{ "quiet", 0, 0, 'q' },
{ "debug", 0, 0, 'd' },
{ 0, 0, 0, 0 }
};
const char *option_help[] = {
"Prints the ATR bytes of the card",
"Lists all configured readers",
"Lists all installed card drivers",
"Recursively lists files stored on card",
"Sends an APDU in format AA:BB:CC:DD:EE:FF...",
"Uses reader number <arg> [0]",
"Forces the use of driver <arg> [auto-detect]",
"Quiet operation",
"Debug output -- may be supplied several times",
};
struct sc_context *ctx = NULL;
struct sc_card *card = NULL;
int list_readers(void)
{
int i;
if (ctx->reader_count == 0) {
printf("No readers configured!\n");
return 0;
}
printf("Configured readers:\n");
for (i = 0; i < ctx->reader_count; i++) {
printf(" %d - %s\n", i, ctx->readers[i]);
}
return 0;
}
int list_drivers(void)
{
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++) {
printf(" %-16s %s\n", ctx->card_drivers[i]->short_name,
ctx->card_drivers[i]->name);
}
return 0;
}
int print_file(struct sc_card *card, const struct sc_file *file, const struct sc_path *path, int depth)
{
int r;
const char *tmps;
const char *ac_ops_df[] = {
"select", "lock", "delete", "create", "rehab", "inval",
"list"
};
const char *ac_ops_ef[] = {
"read", "update", "write", "erase", "rehab", "inval"
};
for (r = 0; r < depth; r++)
printf(" ");
for (r = 0; r < path->len; r++) {
printf("%02X", path->value[r]);
if (r && (r & 1) == 1)
printf(" ");
}
if (file->namelen) {
printf("[");
print_binary(stdout, file->name, file->namelen);
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)"
};
printf("ef structure: %s, ", structs[file->ef_structure]);
}
printf("size: %d\n", file->size);
for (r = 0; r < depth; r++)
printf(" ");
if (file->type == SC_FILE_TYPE_DF)
for (r = 0; r < sizeof(ac_ops_df)/sizeof(ac_ops_df[0]); r++)
printf("%s[%s] ", ac_ops_df[r], acl_to_str(file->acl[r]));
else
for (r = 0; r < sizeof(ac_ops_ef)/sizeof(ac_ops_ef[0]); r++)
printf("%s[%s] ", ac_ops_ef[r], acl_to_str(file->acl[r]));
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 */
hex_dump(stdout, file->sec_attr, file->sec_attr_len, ":");
}
if (file->prop_attr_len) {
printf("\n");
for (r = 0; r < depth; r++)
printf(" ");
printf("prop: ");
hex_dump(stdout, file->prop_attr, file->prop_attr_len, ":");
}
printf("\n\n");
#if 1
if (file->type != SC_FILE_TYPE_DF) {
u8 buf[2048];
if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
r = sc_read_binary(card, 0, buf, file->size, 0);
if (r > 0)
hex_dump_asc(stdout, buf, r, 0);
} else {
r = sc_read_record(card, 0, buf, file->size, 0);
if (r > 0)
hex_dump_asc(stdout, buf, r, 0);
}
}
#endif
return 0;
}
int enum_dir(struct sc_path path, int depth)
{
struct sc_file file;
int r;
u8 files[MAX_BUFFER_SIZE];
file.magic = 0; /* make sure the file is invalid */
r = sc_select_file(card, &path, &file);
if (r) {
fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r));
return 1;
}
if (sc_file_valid(&file))
print_file(card, &file, &path, depth);
if (!sc_file_valid(&file) || file.type == SC_FILE_TYPE_DF) {
int i;
r = sc_list_files(card, files, sizeof(files));
if (r <= 0) {
fprintf(stderr, "sc_list_files() failed: %s\n", sc_strerror(r));
return 1;
}
for (i = 0; i < r/2; i++) {
struct sc_path tmppath;
memcpy(&tmppath, &path, sizeof(path));
memcpy(tmppath.value + tmppath.len, files + 2*i, 2);
tmppath.len += 2;
enum_dir(tmppath, depth + 1);
}
}
return 0;
}
int list_files(void)
{
struct sc_path path;
int r;
sc_format_path("3F00", &path);
r = enum_dir(path, 0);
return r;
}
int send_apdu(void)
{
struct sc_apdu apdu;
u8 buf[MAX_BUFFER_SIZE], sbuf[MAX_BUFFER_SIZE],
rbuf[MAX_BUFFER_SIZE], *p;
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");
return 2;
}
len = len0;
p = buf;
apdu.cla = *p++;
apdu.ins = *p++;
apdu.p1 = *p++;
apdu.p2 = *p++;
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.data = NULL;
apdu.datalen = 0;
apdu.lc = apdu.le = 0;
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) {
fprintf(stderr, "APDU too short (need %d bytes).\n",
apdu.lc-len);
return 2;
}
len -= apdu.lc;
if (len) {
apdu.le = *p++;
if (apdu.le == 0)
apdu.le = 256;
len--;
apdu.cse = SC_APDU_CASE_4_SHORT;
} else
apdu.cse = SC_APDU_CASE_3_SHORT;
if (len) {
fprintf(stderr, "APDU too long (%d bytes extra).\n", len);
return 2;
}
} else if (len == 1) {
apdu.le = *p++;
if (apdu.le == 0)
apdu.le = 256;
len--;
apdu.cse = SC_APDU_CASE_2_SHORT;
} else
apdu.cse = SC_APDU_CASE_1;
printf("Sending: ");
for (r = 0; r < len0; r++)
printf("%02X ", buf[r]);
printf("\n");
#if 0
ctx->debug = 5;
#endif
r = sc_transmit_apdu(card, &apdu);
#if 0
ctx->debug = opt_debug;
#endif
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)
hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
}
return 0;
}
int main(int argc, char * const argv[])
{
int err = 0, r, c, long_optind = 0;
int do_list_readers = 0;
int do_list_drivers = 0;
int do_list_files = 0;
int do_send_apdu = 0;
int do_print_atr = 0;
int action_count = 0;
const char *opt_driver = NULL;
while (1) {
c = getopt_long(argc, argv, "lfr:qds:Dc:a", options, &long_optind);
if (c == -1)
break;
if (c == '?')
print_usage_and_die("opensc-tool");
switch (c) {
case 'l':
do_list_readers = 1;
action_count++;
break;
case 'D':
do_list_drivers = 1;
action_count++;
break;
case 'f':
do_list_files = 1;
action_count++;
break;
case 's':
opt_apdus[opt_apdu_count] = optarg;
do_send_apdu++;
if (opt_apdu_count == 0)
action_count++;
opt_apdu_count++;
break;
case 'a':
do_print_atr = 1;
action_count++;
break;
case 'r':
opt_reader = atoi(optarg);
break;
case 'q':
quiet++;
break;
case 'd':
opt_debug++;
break;
case 'c':
opt_driver = optarg;
break;
}
}
if (action_count == 0)
print_usage_and_die("opensc-tool");
r = sc_establish_context(&ctx);
if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
return 1;
}
ctx->error_file = stderr;
ctx->debug_file = stdout;
ctx->debug = opt_debug;
if (do_list_readers) {
if ((err = list_readers()))
goto end;
action_count--;
}
if (do_list_drivers) {
if ((err = list_drivers()))
goto end;
action_count--;
}
if (action_count <= 0)
goto end;
if (opt_reader >= ctx->reader_count || opt_reader < 0) {
fprintf(stderr, "Illegal reader number. Only %d reader(s) configured.\n", ctx->reader_count);
err = 1;
goto end;
}
if (sc_detect_card(ctx, opt_reader) != 1) {
fprintf(stderr, "Card not present.\n");
err = 3;
goto end;
}
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;
}
}
if (!quiet)
fprintf(stderr, "Connecting to card in reader %s...\n", ctx->readers[opt_reader]);
r = sc_connect_card(ctx, opt_reader, &card);
if (r) {
fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r));
err = 1;
goto end;
}
printf("Using card driver: %s\n", card->driver->name);
r = sc_lock(card);
if (r) {
fprintf(stderr, "Unable to lock card: %s\n", sc_strerror(r));
err = 1;
goto end;
}
if (do_print_atr) {
printf("Card ATR:\n");
hex_dump_asc(stdout, card->atr, card->atr_len, -1);
action_count--;
}
if (do_send_apdu) {
if ((err = send_apdu()))
goto end;
action_count--;
}
if (do_list_files) {
if ((err = list_files()))
goto end;
action_count--;
}
end:
if (card) {
sc_unlock(card);
sc_disconnect_card(card);
}
if (ctx)
sc_destroy_context(ctx);
return err;
}