2002-01-09 01:03:10 +00:00
|
|
|
/*
|
2005-06-16 19:35:31 +00:00
|
|
|
* opensc-explorer.c: A shell for accessing smart cards with libopensc
|
2002-01-09 01:03:10 +00:00
|
|
|
*
|
2006-12-19 21:35:42 +00:00
|
|
|
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
2002-01-09 01:03:10 +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
|
|
|
|
*/
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2004-06-24 17:25:23 +00:00
|
|
|
#include <ctype.h>
|
2002-01-09 01:03:10 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2020-02-08 17:05:14 +00:00
|
|
|
#include <limits.h>
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef ENABLE_READLINE
|
2002-02-10 18:04:03 +00:00
|
|
|
#include <readline/readline.h>
|
2002-03-15 10:40:35 +00:00
|
|
|
#include <readline/history.h>
|
2002-02-10 18:04:03 +00:00
|
|
|
#endif
|
2012-04-26 01:40:06 +00:00
|
|
|
#if !defined(_WIN32)
|
|
|
|
#include <arpa/inet.h> /* for htons() */
|
|
|
|
#endif
|
2010-03-04 08:14:36 +00:00
|
|
|
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <io.h> /* for_setmode() */
|
|
|
|
#include <fcntl.h> /* for _O_TEXT and _O_BINARY */
|
|
|
|
#endif
|
|
|
|
|
2012-08-11 18:27:49 +00:00
|
|
|
#ifdef HAVE_IO_H
|
|
|
|
#include <io.h>
|
|
|
|
#endif
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "libopensc/opensc.h"
|
|
|
|
#include "libopensc/asn1.h"
|
|
|
|
#include "libopensc/cardctl.h"
|
2011-05-27 12:33:52 +00:00
|
|
|
#include "libopensc/cards.h"
|
2018-11-25 21:08:36 +00:00
|
|
|
#include "libopensc/log.h"
|
2020-03-15 17:30:37 +00:00
|
|
|
#include "libopensc/internal.h"
|
2021-02-03 21:50:23 +00:00
|
|
|
#include "libopensc/iso7816.h"
|
2012-08-11 18:27:49 +00:00
|
|
|
#include "common/compat_strlcpy.h"
|
2019-03-14 22:24:23 +00:00
|
|
|
#include <getopt.h>
|
2002-01-09 01:03:10 +00:00
|
|
|
#include "util.h"
|
|
|
|
|
2002-03-15 10:40:35 +00:00
|
|
|
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
|
|
|
|
|
2011-06-01 17:58:55 +00:00
|
|
|
/* type for associations of IDs to names */
|
|
|
|
typedef struct _id2str {
|
|
|
|
unsigned int id;
|
|
|
|
const char *str;
|
|
|
|
} id2str_t;
|
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const char *app_name = "opensc-explorer";
|
2002-03-24 14:12:38 +00:00
|
|
|
|
2010-01-24 15:29:47 +00:00
|
|
|
static int opt_wait = 0, verbose = 0;
|
2007-06-21 12:01:39 +00:00
|
|
|
static const char *opt_driver = NULL;
|
2010-01-24 15:29:47 +00:00
|
|
|
static const char *opt_reader = NULL;
|
2010-08-18 13:42:26 +00:00
|
|
|
static const char *opt_startfile = NULL;
|
2002-01-20 21:20:09 +00:00
|
|
|
|
2007-06-21 12:01:39 +00:00
|
|
|
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;
|
2018-09-16 10:51:50 +00:00
|
|
|
static int interactive = 1;
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2007-06-29 13:19:19 +00:00
|
|
|
static const struct option options[] = {
|
2007-06-21 12:01:39 +00:00
|
|
|
{ "reader", 1, NULL, 'r' },
|
|
|
|
{ "card-driver", 1, NULL, 'c' },
|
2010-08-20 22:51:39 +00:00
|
|
|
{ "mf", 1, NULL, 'm' },
|
2010-05-11 14:30:15 +00:00
|
|
|
{ "wait", 0, NULL, 'w' },
|
2007-06-21 12:01:39 +00:00
|
|
|
{ "verbose", 0, NULL, 'v' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
2002-01-20 21:20:09 +00:00
|
|
|
};
|
2007-06-29 13:19:19 +00:00
|
|
|
static const char *option_help[] = {
|
2002-01-20 21:20:09 +00:00
|
|
|
"Uses reader number <arg> [0]",
|
2019-12-25 18:12:31 +00:00
|
|
|
"Forces the use of driver <arg> [auto-detect; '?' for list]",
|
2010-08-20 22:51:39 +00:00
|
|
|
"Selects path <arg> on start-up, or none if empty [3F00]",
|
2003-01-03 17:07:56 +00:00
|
|
|
"Wait for card insertion",
|
2004-06-13 20:13:12 +00:00
|
|
|
"Verbose operation. Use several times to enable debug output.",
|
2002-01-20 21:20:09 +00:00
|
|
|
};
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2006-10-30 18:51:48 +00:00
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
/* declare functions called by user commands */
|
2012-05-20 16:05:16 +00:00
|
|
|
static int do_echo(int argc, char **argv);
|
2011-06-02 11:21:25 +00:00
|
|
|
static int do_ls(int argc, char **argv);
|
2012-04-26 01:40:06 +00:00
|
|
|
static int do_find(int argc, char **argv);
|
2015-07-30 06:19:01 +00:00
|
|
|
static int do_find_tags(int argc, char **argv);
|
2011-06-02 11:21:25 +00:00
|
|
|
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);
|
2018-06-17 10:52:19 +00:00
|
|
|
static int do_pininfo(int argc, char **argv);
|
2011-06-02 11:21:25 +00:00
|
|
|
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);
|
2020-01-12 09:40:05 +00:00
|
|
|
static int do_get_record(int argc, char **argv);
|
2011-06-02 11:21:25 +00:00
|
|
|
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);
|
2013-01-06 12:13:08 +00:00
|
|
|
static int do_sm(int argc, char **argv);
|
2011-06-02 11:21:25 +00:00
|
|
|
static int do_asn1(int argc, char **argv);
|
|
|
|
static int do_help(int argc, char **argv);
|
|
|
|
static int do_quit(int argc, char **argv);
|
|
|
|
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
struct command {
|
2002-03-15 10:40:35 +00:00
|
|
|
int (*func)(int, char **);
|
2011-06-02 11:21:25 +00:00
|
|
|
const char * name;
|
|
|
|
const char * args;
|
2002-02-21 18:53:23 +00:00
|
|
|
const char * help;
|
2002-01-09 01:03:10 +00:00
|
|
|
};
|
2002-01-10 23:02:48 +00:00
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
static struct command cmds[] = {
|
2012-05-20 16:05:16 +00:00
|
|
|
{ do_echo,
|
2018-07-07 20:04:13 +00:00
|
|
|
"echo", "[<string> ...]",
|
2012-05-20 16:05:16 +00:00
|
|
|
"display arguments" },
|
2011-06-02 11:21:25 +00:00
|
|
|
{ do_ls,
|
2018-07-07 20:04:13 +00:00
|
|
|
"ls", "[<pattern> ...]",
|
2012-05-28 16:49:05 +00:00
|
|
|
"list files in the current DF" },
|
2012-04-26 01:40:06 +00:00
|
|
|
{ do_find,
|
2018-07-07 20:04:13 +00:00
|
|
|
"find", "[<start-id> [<end-id>]]",
|
2012-04-26 01:40:06 +00:00
|
|
|
"find all files in the current DF" },
|
2015-07-30 06:19:01 +00:00
|
|
|
{ do_find_tags,
|
2018-07-07 20:04:13 +00:00
|
|
|
"find_tags", "[<start-tag> [<end-tag>]]",
|
2015-07-30 06:19:01 +00:00
|
|
|
"find all tags of data objects in the current context" },
|
2011-06-02 11:21:25 +00:00
|
|
|
{ do_cd,
|
2018-07-07 20:04:13 +00:00
|
|
|
"cd", "{.. | <file-id> | aid:<DF-name>}",
|
2011-06-02 11:21:25 +00:00
|
|
|
"change to another DF" },
|
|
|
|
{ do_cat,
|
2020-02-02 12:41:40 +00:00
|
|
|
"cat", "[{<file-id> | sfi:<sfi-id>} [<rec-no>]]"
|
|
|
|
, "print the contents of [a record in] an EF" },
|
2011-06-02 11:21:25 +00:00
|
|
|
{ do_info,
|
2018-07-07 20:04:13 +00:00
|
|
|
"info", "[<file-id>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"display attributes of card file" },
|
|
|
|
{ do_create,
|
2018-07-07 20:04:13 +00:00
|
|
|
"create", "<file-id> <size>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"create a new EF" },
|
|
|
|
{ do_mkdir,
|
2018-07-07 20:04:13 +00:00
|
|
|
"mkdir", "<file-id> <size>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"create a new DF" },
|
|
|
|
{ do_delete,
|
2018-07-07 20:04:13 +00:00
|
|
|
"delete", "<file-id>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"remove an EF/DF" },
|
|
|
|
{ do_delete,
|
2018-07-07 20:04:13 +00:00
|
|
|
"rm", "<file-id>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"remove an EF/DF" },
|
2018-06-17 10:52:19 +00:00
|
|
|
{ do_pininfo,
|
2018-07-07 20:04:13 +00:00
|
|
|
"pin_info", "{CHV|KEY|AUT|PRO}<key-ref>",
|
2018-06-17 10:52:19 +00:00
|
|
|
"get information on PIN or key from the card" },
|
2011-06-02 11:21:25 +00:00
|
|
|
{ do_verify,
|
2018-07-07 20:04:13 +00:00
|
|
|
"verify", "{CHV|KEY|AUT|PRO}<key-ref> [<pin>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"present a PIN or key to the card" },
|
|
|
|
{ do_change,
|
2018-07-07 20:04:13 +00:00
|
|
|
"change", "CHV<pin-ref> [[<old-pin>] <new-pin>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"change a PIN" },
|
|
|
|
{ do_unblock,
|
2018-07-07 20:04:13 +00:00
|
|
|
"unblock", "CHV<pin-ref> [<puk> [<new-pin>]]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"unblock a PIN" },
|
|
|
|
{ do_put,
|
2018-07-07 20:04:13 +00:00
|
|
|
"put", "<file-id> [<input-file>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"copy a local file to the card" },
|
|
|
|
{ do_get,
|
2018-07-07 20:04:13 +00:00
|
|
|
"get", "<file-id> [<output-file>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"copy an EF to a local file" },
|
2020-01-12 09:40:05 +00:00
|
|
|
{ do_get_record,
|
|
|
|
"get_record", "<file-id> <rec-no> [<output-file>]",
|
|
|
|
"copy a record of an EF to a local file" },
|
2011-06-02 11:21:25 +00:00
|
|
|
{ do_get_data,
|
2018-07-07 20:04:13 +00:00
|
|
|
"do_get", "<hex-tag> [<output-file>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"get a data object" },
|
|
|
|
{ do_put_data,
|
2018-07-07 20:04:13 +00:00
|
|
|
"do_put", "<hex-tag> <data>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"put a data object" },
|
|
|
|
{ do_erase,
|
|
|
|
"erase", "",
|
|
|
|
"erase card" },
|
|
|
|
{ do_random,
|
2018-07-08 08:40:06 +00:00
|
|
|
"random", "<count> [<output-file>]",
|
2011-06-02 11:21:25 +00:00
|
|
|
"obtain <count> random bytes from card" },
|
|
|
|
{ do_update_record,
|
2018-07-07 20:04:13 +00:00
|
|
|
"update_record", "<file-id> <rec-no> <rec-offs> <data>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"update record" },
|
|
|
|
{ do_update_binary,
|
2018-07-07 20:04:13 +00:00
|
|
|
"update_binary", "<file-id> <offs> <data>",
|
2011-06-02 11:21:25 +00:00
|
|
|
"update binary" },
|
|
|
|
{ do_apdu,
|
2018-07-07 20:04:13 +00:00
|
|
|
"apdu", "<data> ...",
|
2011-06-02 11:21:25 +00:00
|
|
|
"send a custom apdu command" },
|
|
|
|
{ do_asn1,
|
2020-05-21 08:50:53 +00:00
|
|
|
"asn1", "[<file-id> [<rec-no>] [<offs>]]",
|
2020-03-15 17:30:37 +00:00
|
|
|
"decode an ASN.1 file or record" },
|
2013-01-06 12:13:08 +00:00
|
|
|
{ do_sm,
|
2018-07-07 20:04:13 +00:00
|
|
|
"sm", "{open|close}",
|
2013-01-06 12:13:08 +00:00
|
|
|
"call SM 'open' or 'close' handlers, if available"},
|
2011-06-02 11:21:25 +00:00
|
|
|
{ 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 }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-02-02 11:57:08 +00:00
|
|
|
static char *path_to_filename(const sc_path_t *path, const char sep, size_t rec)
|
2011-06-02 17:47:50 +00:00
|
|
|
{
|
2020-02-02 11:57:08 +00:00
|
|
|
static char buf[2*SC_MAX_PATH_STRING_SIZE+10];
|
2011-06-15 18:53:50 +00:00
|
|
|
size_t i, j;
|
2011-06-02 17:47:50 +00:00
|
|
|
|
2020-02-02 11:57:08 +00:00
|
|
|
/* build file name from path elements */
|
2011-06-02 17:47:50 +00:00
|
|
|
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]);
|
|
|
|
}
|
2020-02-02 11:57:08 +00:00
|
|
|
/* single record: append record-number */
|
|
|
|
if (rec > 0)
|
|
|
|
j += sprintf(buf+j, "%c%"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
|
(sep == '-') ? '_' : '-', rec);
|
2011-06-15 18:53:50 +00:00
|
|
|
buf[j] = '\0';
|
2011-06-02 17:47:50 +00:00
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2011-06-02 13:39:12 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static void die(int ret)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(current_file);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (card) {
|
2010-01-24 15:29:47 +00:00
|
|
|
sc_disconnect_card(card);
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
if (ctx)
|
2002-03-24 14:12:38 +00:00
|
|
|
sc_release_context(ctx);
|
2002-01-09 01:03:10 +00:00
|
|
|
exit(ret);
|
|
|
|
}
|
|
|
|
|
2010-11-06 16:53:11 +00:00
|
|
|
static void select_current_path_or_die(void)
|
2010-08-20 22:51:42 +00:00
|
|
|
{
|
|
|
|
if (current_path.type || current_path.len) {
|
2018-05-18 21:21:14 +00:00
|
|
|
int r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, ¤t_path, NULL);
|
|
|
|
sc_unlock(card);
|
2010-08-20 22:51:42 +00:00
|
|
|
if (r) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "unable to select parent DF: %s\n", sc_strerror(r));
|
2010-08-20 22:51:42 +00:00
|
|
|
die(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
static struct command *
|
2018-07-14 19:05:54 +00:00
|
|
|
ambiguous_match(struct command *table, const char *cmd, int *ambiguous)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2002-02-21 18:53:23 +00:00
|
|
|
struct command *last_match = NULL;
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2018-07-14 19:05:54 +00:00
|
|
|
if (table != NULL && cmd != NULL) {
|
|
|
|
for (; table->name; table++) {
|
|
|
|
size_t cmdlen = strlen(cmd);
|
|
|
|
|
|
|
|
/* compare cmd with prefix of known command */
|
|
|
|
if (strncasecmp(table->name, cmd, cmdlen) == 0) {
|
|
|
|
/* succeed immediately if lengths match too, i.e. exact match */
|
|
|
|
if (cmdlen == strlen(table->name))
|
|
|
|
return table;
|
|
|
|
/* fail on multiple prefix-only matches */
|
|
|
|
if (last_match != NULL) {
|
|
|
|
if (ambiguous != NULL)
|
|
|
|
*ambiguous = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* remember latest prefix-only match */
|
|
|
|
last_match = table;
|
|
|
|
}
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-14 19:05:54 +00:00
|
|
|
/* indicate as non-ambiguous if there was no match */
|
|
|
|
if (ambiguous != NULL && last_match == NULL)
|
|
|
|
*ambiguous = 0;
|
2002-01-09 01:03:10 +00:00
|
|
|
return last_match;
|
|
|
|
}
|
|
|
|
|
2013-08-02 20:01:51 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
check_ret(int r, int op, const char *err, const sc_file_t *file)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: %s\n", err, sc_strerror(r));
|
|
|
|
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)
|
2008-03-06 16:06:59 +00:00
|
|
|
fprintf(stderr, "ACL for operation: %s\n", util_acl_to_str(sc_file_get_acl_entry(file, op)));
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
|
2013-08-02 20:01:51 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
arg_to_fid(const char *arg, u8 *fid)
|
2012-04-26 01:40:06 +00:00
|
|
|
{
|
2013-08-02 20:01:51 +00:00
|
|
|
unsigned int fid0, fid1;
|
|
|
|
|
|
|
|
if (strlen(arg) != 4) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Wrong ID length.\n");
|
2013-08-02 20:01:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sscanf(arg, "%02X%02X", &fid0, &fid1) != 2) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid ID.\n");
|
2013-08-02 20:01:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fid[0] = (unsigned char)fid0;
|
|
|
|
fid[1] = (unsigned char)fid1;
|
|
|
|
|
|
|
|
return 0;
|
2012-04-26 01:40:06 +00:00
|
|
|
}
|
2013-08-02 20:01:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
arg_to_path(const char *arg, sc_path_t *path, int is_id)
|
2002-01-13 23:56:13 +00:00
|
|
|
{
|
2011-01-09 08:49:15 +00:00
|
|
|
memset(path, 0, sizeof(sc_path_t));
|
|
|
|
|
2006-10-30 18:51:48 +00:00
|
|
|
if (strncasecmp(arg, "aid:", strlen("aid:")) == 0) {
|
2006-11-09 21:26:19 +00:00
|
|
|
/* DF aid */
|
2006-10-30 18:51:48 +00:00
|
|
|
const char *p = arg + strlen("aid:");
|
2011-06-02 14:13:04 +00:00
|
|
|
int r;
|
|
|
|
|
2006-10-30 18:51:48 +00:00
|
|
|
path->type = SC_PATH_TYPE_DF_NAME;
|
2011-06-02 14:13:04 +00:00
|
|
|
path->len = sizeof(path->value);
|
|
|
|
if ((r = sc_hex_to_bin(p, path->value, &path->len)) < 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Error parsing AID: %s\n", p);
|
2011-06-02 14:13:04 +00:00
|
|
|
return r;
|
|
|
|
}
|
2002-02-11 15:55:34 +00:00
|
|
|
} else {
|
2006-11-09 21:26:19 +00:00
|
|
|
/* file id */
|
2006-10-30 18:51:48 +00:00
|
|
|
u8 cbuf[2];
|
2020-02-08 15:59:30 +00:00
|
|
|
|
|
|
|
if (arg_to_fid(arg, cbuf) < 0)
|
2006-10-30 18:51:48 +00:00
|
|
|
return -1;
|
2012-04-26 01:40:06 +00:00
|
|
|
|
2006-10-30 18:51:48 +00:00
|
|
|
if ((cbuf[0] == 0x3F && cbuf[1] == 0x00) || is_id) {
|
|
|
|
path->len = 2;
|
|
|
|
memcpy(path->value, cbuf, 2);
|
2011-06-02 16:20:58 +00:00
|
|
|
path->type = (is_id) ? SC_PATH_TYPE_FILE_ID : SC_PATH_TYPE_PATH;
|
2006-10-30 18:51:48 +00:00
|
|
|
} else {
|
|
|
|
*path = current_path;
|
2020-02-08 15:59:30 +00:00
|
|
|
if (path->type == SC_PATH_TYPE_DF_NAME) {
|
|
|
|
if (path->len > sizeof(path->aid.value)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid length of DF_NAME path\n");
|
2011-04-23 06:32:53 +00:00
|
|
|
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;
|
|
|
|
}
|
2006-10-30 18:51:48 +00:00
|
|
|
sc_append_path_id(path, cbuf, 2);
|
|
|
|
}
|
2002-02-11 15:55:34 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
return 0;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
|
|
|
|
2005-03-09 00:04:44 +00:00
|
|
|
static void print_file(const sc_file_t *file)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2011-03-31 07:45:08 +00:00
|
|
|
const char *format = " %02X%02X ";
|
2016-12-15 15:22:11 +00:00
|
|
|
const char *st = NULL;
|
|
|
|
|
|
|
|
if(!file->type_attr_len)
|
|
|
|
st = "???";
|
2011-03-31 07:45:08 +00:00
|
|
|
|
2002-01-09 01:03:10 +00:00
|
|
|
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:
|
2011-03-31 07:45:08 +00:00
|
|
|
format = "[%02X%02X]";
|
2002-01-09 01:03:10 +00:00
|
|
|
st = "DF";
|
|
|
|
break;
|
|
|
|
}
|
2011-03-31 07:45:08 +00:00
|
|
|
printf(format, file->id >> 8, file->id & 0xFF);
|
2016-12-15 15:22:11 +00:00
|
|
|
if (st == NULL)
|
|
|
|
printf("\t0x%02X", *file->type_attr);
|
|
|
|
else
|
|
|
|
printf("\t%4s", st);
|
2006-08-02 19:43:13 +00:00
|
|
|
printf(" %5lu", (unsigned long)file->size);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (file->namelen) {
|
|
|
|
printf("\tName: ");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_print_binary(stdout, file->name, file->namelen);
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
2005-02-11 20:09:34 +00:00
|
|
|
printf("\n");
|
2002-01-09 01:03:10 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-20 16:05:16 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-05-28 16:49:05 +00:00
|
|
|
static int pattern_match(const char *pattern, const char *string)
|
|
|
|
{
|
|
|
|
if (pattern == NULL || string == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (*pattern != '\0' && *string != '\0') {
|
2018-04-14 17:38:34 +00:00
|
|
|
/* wildcard matching multiple characters */
|
2012-05-28 16:49:05 +00:00
|
|
|
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++) {
|
2020-06-16 12:24:57 +00:00
|
|
|
if (tolower((unsigned char) *pattern) == tolower((unsigned char) *string))
|
2012-05-28 16:49:05 +00:00
|
|
|
match++;
|
|
|
|
}
|
|
|
|
if (!match)
|
|
|
|
return 0;
|
|
|
|
pattern++;
|
|
|
|
string++;
|
|
|
|
}
|
|
|
|
/* single character comparison / wildcard matching a single character */
|
2020-06-16 12:24:57 +00:00
|
|
|
else if (tolower((unsigned char) *pattern) == tolower((unsigned char) *string) || *pattern == '?') {
|
2012-05-28 16:49:05 +00:00
|
|
|
pattern++;
|
|
|
|
string++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (*string == '\0' || *pattern == '\0')
|
|
|
|
break;
|
|
|
|
}
|
2020-06-16 12:24:57 +00:00
|
|
|
return (*pattern != '\0' || *string != '\0' || tolower((unsigned char) *pattern) != tolower((unsigned char) *string)) ? 0 : 1;
|
2012-05-28 16:49:05 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_ls(int argc, char **argv)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2020-02-08 14:56:36 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_RESP_SIZE], *cur = buf;
|
2002-01-09 01:03:10 +00:00
|
|
|
int r, count;
|
|
|
|
|
2020-02-08 14:56:36 +00:00
|
|
|
memset(buf, 0, sizeof(buf));
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_list_files(card, buf, sizeof(buf));
|
|
|
|
sc_unlock(card);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (r < 0) {
|
2020-02-08 14:56:36 +00:00
|
|
|
check_ret(r, SC_AC_OP_LIST_FILES, "Unable to receive file listing", current_file);
|
2002-01-09 01:03:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
count = r;
|
2005-02-11 20:09:34 +00:00
|
|
|
printf("FileID\tType Size\n");
|
2002-01-09 01:03:10 +00:00
|
|
|
while (count >= 2) {
|
2020-02-08 14:56:36 +00:00
|
|
|
char filename[SC_MAX_PATH_STRING_SIZE];
|
2012-05-28 16:49:05 +00:00
|
|
|
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;
|
2006-10-30 18:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2012-05-28 16:49:05 +00:00
|
|
|
/* if any filename pattern were given, filter only matching file names */
|
|
|
|
if (argc == 0 || matches) {
|
2020-02-08 14:56:36 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file = NULL;
|
|
|
|
|
2012-05-28 16:49:05 +00:00
|
|
|
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) {
|
2020-02-08 14:56:36 +00:00
|
|
|
fprintf(stderr, "Unable to set path.\n");
|
2012-05-28 16:49:05 +00:00
|
|
|
die(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2012-05-28 16:49:05 +00:00
|
|
|
if (r) {
|
2020-02-08 14:56:36 +00:00
|
|
|
fprintf(stderr, "Unable to select file %02X%02X: %s\n",
|
|
|
|
cur[0], cur[1], sc_strerror(r));
|
2012-05-28 16:49:05 +00:00
|
|
|
} else {
|
|
|
|
file->id = (cur[0] << 8) | cur[1];
|
2020-02-08 14:56:36 +00:00
|
|
|
print_file(file);
|
2012-05-28 16:49:05 +00:00
|
|
|
sc_file_free(file);
|
|
|
|
}
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
2005-02-11 20:09:34 +00:00
|
|
|
cur += 2;
|
2002-01-09 01:03:10 +00:00
|
|
|
count -= 2;
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
2005-02-11 20:09:34 +00:00
|
|
|
return 0;
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
|
2012-04-26 01:40:06 +00:00
|
|
|
static int do_find(int argc, char **argv)
|
|
|
|
{
|
2020-02-08 13:20:31 +00:00
|
|
|
u8 fid[2] = { 0x00, 0x00 };
|
|
|
|
u8 end[2] = { 0xFF, 0xFF };
|
2012-08-11 18:27:49 +00:00
|
|
|
sc_path_t path;
|
|
|
|
int r;
|
2012-04-26 01:40:06 +00:00
|
|
|
|
2012-08-11 18:27:49 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-04-26 01:40:06 +00:00
|
|
|
|
|
|
|
printf("FileID\tType Size\n");
|
|
|
|
while (1) {
|
|
|
|
sc_file_t *file = NULL;
|
|
|
|
|
2012-08-11 18:27:49 +00:00
|
|
|
printf("(%02X%02X)\r", fid[0], fid[1]);
|
|
|
|
fflush(stdout);
|
2012-04-26 01:40:06 +00:00
|
|
|
|
|
|
|
if (current_path.type != SC_PATH_TYPE_DF_NAME) {
|
|
|
|
path = current_path;
|
2020-02-08 13:20:31 +00:00
|
|
|
sc_append_path_id(&path, fid, sizeof(fid));
|
2012-04-26 01:40:06 +00:00
|
|
|
} else {
|
2020-02-08 13:20:31 +00:00
|
|
|
if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0) != SC_SUCCESS) {
|
|
|
|
fprintf(stderr, "Unable to set path.\n");
|
2012-04-26 01:40:06 +00:00
|
|
|
die(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2012-04-26 01:40:06 +00:00
|
|
|
switch (r) {
|
2012-08-11 18:27:49 +00:00
|
|
|
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;
|
2012-04-26 01:40:06 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-30 06:19:01 +00:00
|
|
|
static int do_find_tags(int argc, char **argv)
|
|
|
|
{
|
2020-02-08 13:28:23 +00:00
|
|
|
u8 start[2] = { 0x00, 0x00 };
|
|
|
|
u8 end[2] = { 0xFF, 0xFF };
|
|
|
|
u8 rbuf[SC_MAX_EXT_APDU_RESP_SIZE];
|
2015-07-30 06:19:01 +00:00
|
|
|
int r;
|
|
|
|
unsigned int tag, tag_end;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
2020-02-08 13:28:23 +00:00
|
|
|
r = sc_get_data(card, tag, rbuf, sizeof(rbuf));
|
2020-01-31 21:03:12 +00:00
|
|
|
else
|
|
|
|
r = SC_ERROR_READER_LOCKED;
|
2018-05-18 21:21:14 +00:00
|
|
|
sc_unlock(card);
|
2015-07-30 06:19:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_cd(int argc, char **argv)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2002-01-09 01:03:10 +00:00
|
|
|
int r;
|
|
|
|
|
2005-02-11 20:09:34 +00:00
|
|
|
if (argc != 1)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_cd);
|
|
|
|
|
2002-03-15 10:40:35 +00:00
|
|
|
if (strcmp(argv[0], "..") == 0) {
|
2011-04-23 06:32:53 +00:00
|
|
|
path = current_path;
|
|
|
|
if (path.len < 4) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "unable to go up, already in MF.\n");
|
2002-01-09 01:03:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2011-04-23 06:32:53 +00:00
|
|
|
|
2020-02-02 10:22:49 +00:00
|
|
|
if (path.type == SC_PATH_TYPE_DF_NAME) {
|
2011-04-23 06:32:53 +00:00
|
|
|
sc_format_path("3F00", &path);
|
|
|
|
}
|
2020-02-02 10:22:49 +00:00
|
|
|
else {
|
2011-04-23 06:32:53 +00:00
|
|
|
path.len -= 2;
|
|
|
|
}
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (r) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "unable to go up: %s\n", sc_strerror(r));
|
2002-01-09 01:03:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(current_file);
|
2002-02-20 09:56:47 +00:00
|
|
|
current_file = file;
|
2002-01-09 01:03:10 +00:00
|
|
|
current_path = path;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_cd);
|
2002-03-15 10:40:35 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (r) {
|
2002-02-20 09:56:47 +00:00
|
|
|
check_ret(r, SC_AC_OP_SELECT, "unable to select DF", current_file);
|
2002-01-09 01:03:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2020-02-02 10:22:49 +00:00
|
|
|
if ((file->type != SC_FILE_TYPE_DF) && (file->type != SC_FILE_TYPE_UNKNOWN) &&
|
|
|
|
(card->type != SC_CARD_TYPE_BELPIC_EID)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Error: file is not a DF.\n");
|
2002-02-20 09:56:47 +00:00
|
|
|
sc_file_free(file);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2002-01-10 12:33:56 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2002-02-11 15:55:34 +00:00
|
|
|
current_path = path;
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(current_file);
|
2002-02-20 09:56:47 +00:00
|
|
|
current_file = file;
|
2002-01-09 01:03:10 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-02 12:57:27 +00:00
|
|
|
static int read_and_print_binary_file(sc_file_t *file)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2020-02-02 12:57:27 +00:00
|
|
|
u8 *buf;
|
|
|
|
size_t size = (file->size > 0) ? file->size : SC_MAX_EXT_APDU_RESP_SIZE;
|
2018-07-17 11:31:14 +00:00
|
|
|
int r, ret = -1;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2020-02-02 12:57:27 +00:00
|
|
|
buf = calloc(size, 1);
|
|
|
|
if (buf == NULL)
|
2012-06-01 13:37:06 +00:00
|
|
|
return -1;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_read_binary(card, 0, buf, size, 0);
|
|
|
|
sc_unlock(card);
|
2020-02-02 12:57:27 +00:00
|
|
|
if (r < 0) {
|
|
|
|
check_ret(r, SC_AC_OP_READ, "Read failed", file);
|
2018-07-17 11:31:14 +00:00
|
|
|
goto err;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
2012-06-01 13:37:06 +00:00
|
|
|
|
|
|
|
util_hex_dump_asc(stdout, buf, r, 0);
|
|
|
|
|
2018-07-17 11:31:14 +00:00
|
|
|
ret = 0;
|
2020-02-02 12:57:27 +00:00
|
|
|
|
2018-07-17 11:31:14 +00:00
|
|
|
err:
|
2012-06-01 13:37:06 +00:00
|
|
|
free(buf);
|
2018-07-17 11:31:14 +00:00
|
|
|
return ret;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2020-02-02 12:41:40 +00:00
|
|
|
static int read_and_print_record_file(sc_file_t *file, unsigned char sfi, unsigned int wanted)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2020-02-08 18:21:59 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_RESP_SIZE];
|
2020-02-02 12:41:40 +00:00
|
|
|
int r;
|
|
|
|
unsigned int rec;
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2020-02-02 12:41:40 +00:00
|
|
|
for (rec = (wanted > 0) ? wanted : 1; wanted == 0 || wanted == rec; rec++) {
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_read_record(card, rec, buf, sizeof(buf),
|
|
|
|
SC_RECORD_BY_REC_NR | sfi);
|
2020-01-31 21:03:12 +00:00
|
|
|
else
|
|
|
|
r = SC_ERROR_READER_LOCKED;
|
2018-05-18 21:21:14 +00:00
|
|
|
sc_unlock(card);
|
2020-02-02 12:41:40 +00:00
|
|
|
if (r == SC_ERROR_RECORD_NOT_FOUND && wanted == 0)
|
2002-02-10 18:04:03 +00:00
|
|
|
return 0;
|
|
|
|
if (r < 0) {
|
2020-02-08 18:21:59 +00:00
|
|
|
check_ret(r, SC_AC_OP_READ, "Read failed", file);
|
2002-02-10 18:04:03 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
printf("Record %d:\n", rec);
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, buf, r, 0);
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
2020-02-08 18:21:59 +00:00
|
|
|
|
|
|
|
return 0;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_cat(int argc, char **argv)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2008-05-26 10:46:16 +00:00
|
|
|
int r, err = 1;
|
2020-02-02 12:41:40 +00:00
|
|
|
unsigned int rec = 0;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
2008-05-26 10:46:16 +00:00
|
|
|
sc_file_t *file = NULL;
|
2002-01-09 01:03:10 +00:00
|
|
|
int not_current = 1;
|
2010-08-21 20:12:59 +00:00
|
|
|
int sfi = 0;
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2020-02-02 12:41:40 +00:00
|
|
|
if (argc > 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_cat);
|
|
|
|
|
2020-02-02 12:41:40 +00:00
|
|
|
if (argc > 1)
|
|
|
|
rec = (unsigned int) strtoul(argv[1], NULL, 10);
|
|
|
|
|
2002-03-15 10:40:35 +00:00
|
|
|
if (!argc) {
|
2002-01-09 01:03:10 +00:00
|
|
|
path = current_path;
|
|
|
|
file = current_file;
|
|
|
|
not_current = 0;
|
|
|
|
} else {
|
2011-03-31 07:45:13 +00:00
|
|
|
const char sfi_prefix[] = "sfi:";
|
|
|
|
|
|
|
|
if (strncasecmp(argv[0], sfi_prefix, strlen(sfi_prefix)) == 0) {
|
|
|
|
const char *sfi_n = argv[0] + strlen(sfi_prefix);
|
2010-09-13 08:08:42 +00:00
|
|
|
|
2010-08-21 20:12:59 +00:00
|
|
|
if(!current_file) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "A DF must be selected to read by SFI\n");
|
2010-08-21 20:12:59 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
path = current_path;
|
|
|
|
file = current_file;
|
|
|
|
not_current = 0;
|
|
|
|
sfi = atoi(sfi_n);
|
|
|
|
if ((sfi < 1) || (sfi > 30)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid SFI: %s\n", sfi_n);
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_cat);
|
2010-08-21 20:12:59 +00:00
|
|
|
}
|
2011-03-31 07:45:13 +00:00
|
|
|
} else {
|
2011-03-31 07:45:19 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_cat);
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2011-03-31 07:45:13 +00:00
|
|
|
if (r) {
|
|
|
|
check_ret(r, SC_AC_OP_SELECT, "unable to select file",
|
|
|
|
current_file);
|
|
|
|
goto err;
|
|
|
|
}
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-21 20:12:59 +00:00
|
|
|
if (file->type != SC_FILE_TYPE_WORKING_EF &&
|
|
|
|
!(file->type == SC_FILE_TYPE_DF && sfi)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "only working EFs may be read\n");
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2002-02-26 11:23:25 +00:00
|
|
|
}
|
2020-02-02 12:41:40 +00:00
|
|
|
if (file->ef_structure == SC_FILE_EF_TRANSPARENT && !sfi) {
|
|
|
|
if (argc > 1) {
|
|
|
|
fprintf(stderr, "Transparent files do not have records\n");
|
|
|
|
goto err;
|
|
|
|
}
|
2020-02-02 12:57:27 +00:00
|
|
|
read_and_print_binary_file(file);
|
2020-02-02 12:41:40 +00:00
|
|
|
}
|
2002-02-10 18:04:03 +00:00
|
|
|
else
|
2020-02-02 12:41:40 +00:00
|
|
|
read_and_print_record_file(file, sfi, rec);
|
2008-05-26 10:46:16 +00:00
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
err = 0;
|
2008-05-26 10:46:16 +00:00
|
|
|
err:
|
2002-01-09 01:03:10 +00:00
|
|
|
if (not_current) {
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(file);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
2008-05-26 10:46:16 +00:00
|
|
|
|
|
|
|
return -err;
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_info(int argc, char **argv)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_file_t *file;
|
|
|
|
sc_path_t path;
|
2005-02-04 18:10:23 +00:00
|
|
|
size_t i;
|
2016-12-15 15:22:11 +00:00
|
|
|
const char *st = NULL;
|
2005-02-04 18:10:23 +00:00
|
|
|
int r, not_current = 1;
|
2011-06-01 17:58:55 +00:00
|
|
|
const id2str_t *ac_ops = NULL;
|
2002-03-15 10:40:35 +00:00
|
|
|
|
2021-01-06 07:43:29 +00:00
|
|
|
const char *lifecycle = "unknown value";
|
|
|
|
|
|
|
|
static const id2str_t lc[] = {
|
|
|
|
{ SC_FILE_STATUS_NO_INFO, "No information given" },
|
|
|
|
{ SC_FILE_STATUS_CREATION, "Creation state" },
|
|
|
|
{ SC_FILE_STATUS_RFU_2, "RFU (2)" },
|
|
|
|
{ SC_FILE_STATUS_INITIALISATION, "Initialisation state" },
|
|
|
|
{ SC_FILE_STATUS_INVALIDATED, "Operational, deactivated" },
|
|
|
|
{ SC_FILE_STATUS_ACTIVATED, "Operational, activated" },
|
|
|
|
{ SC_FILE_STATUS_RFU_8, "RFU (8)" },
|
|
|
|
{ SC_FILE_STATUS_RFU_9, "RFU (9)" },
|
|
|
|
{ SC_FILE_STATUS_RFU_10, "RFU (10)" },
|
|
|
|
{ SC_FILE_STATUS_RFU_11, "RFU (11)" },
|
|
|
|
{ SC_FILE_STATUS_TERMINATION, "Termination state" },
|
|
|
|
{ SC_FILE_STATUS_PROPRIETARY, "Proprietary state" },
|
|
|
|
{ SC_FILE_STATUS_UNKNOWN, "LCSB is not present" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2002-03-15 10:40:35 +00:00
|
|
|
if (!argc) {
|
2002-01-09 01:03:10 +00:00
|
|
|
path = current_path;
|
|
|
|
file = current_file;
|
|
|
|
not_current = 0;
|
2002-03-15 10:40:35 +00:00
|
|
|
} else if (argc == 1) {
|
2012-04-02 22:00:56 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_info);
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (r) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "unable to select file: %s\n", sc_strerror(r));
|
2002-01-09 01:03:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-04-02 22:00:56 +00:00
|
|
|
} else
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_info);
|
2002-03-15 10:40:35 +00:00
|
|
|
|
2020-02-02 13:42:22 +00:00
|
|
|
if (!file->type_attr_len)
|
2016-12-15 15:22:11 +00:00
|
|
|
st = "Unknown File";
|
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
switch (file->type) {
|
2002-01-09 01:03:10 +00:00
|
|
|
case SC_FILE_TYPE_WORKING_EF:
|
2020-02-02 13:42:22 +00:00
|
|
|
st = "Working Elementary File";
|
|
|
|
break;
|
2002-01-09 01:03:10 +00:00
|
|
|
case SC_FILE_TYPE_INTERNAL_EF:
|
2020-02-02 13:42:22 +00:00
|
|
|
st = "Internal Elementary File";
|
2002-01-09 01:03:10 +00:00
|
|
|
break;
|
|
|
|
case SC_FILE_TYPE_DF:
|
|
|
|
st = "Dedicated File";
|
|
|
|
break;
|
|
|
|
}
|
2016-12-15 15:22:11 +00:00
|
|
|
if (st == NULL)
|
|
|
|
printf("\nFile type [%02x] ID %04X", *file->type_attr, file->id);
|
|
|
|
else
|
|
|
|
printf("\n%s ID %04X", st, file->id);
|
2015-03-31 12:17:40 +00:00
|
|
|
if (file->sid)
|
|
|
|
printf(", SFI %02X", file->sid);
|
2020-02-02 11:57:08 +00:00
|
|
|
printf("\n\n%-25s%s\n", "File path:", path_to_filename(&path, '/', 0));
|
2020-02-02 13:42:22 +00:00
|
|
|
printf("%-25s%"SC_FORMAT_LEN_SIZE_T"u bytes\n", "File size:", file->size);
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
if (file->type == SC_FILE_TYPE_DF) {
|
2011-06-01 17:58:55 +00:00
|
|
|
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 }
|
2002-01-09 01:03:10 +00:00
|
|
|
};
|
2011-06-01 17:58:55 +00:00
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
if (file->namelen) {
|
2020-02-02 13:42:22 +00:00
|
|
|
printf("%-25s", "DF name:");
|
2008-03-06 16:06:59 +00:00
|
|
|
util_print_binary(stdout, file->name, file->namelen);
|
2002-01-09 01:03:10 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
2011-06-01 17:58:55 +00:00
|
|
|
|
|
|
|
ac_ops = ac_ops_df;
|
2002-01-09 01:03:10 +00:00
|
|
|
} else {
|
2011-06-01 17:58:55 +00:00
|
|
|
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 }
|
2005-02-11 20:09:34 +00:00
|
|
|
};
|
2011-06-01 17:58:55 +00:00
|
|
|
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 }
|
2002-01-13 23:56:13 +00:00
|
|
|
};
|
2011-06-01 17:58:55 +00:00
|
|
|
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;
|
2020-02-02 13:42:22 +00:00
|
|
|
printf("%-25s%s\n", "EF structure:", ef_type);
|
|
|
|
|
|
|
|
if (file->record_count > 0)
|
|
|
|
printf("%-25s%"SC_FORMAT_LEN_SIZE_T"u\n",
|
|
|
|
"Number of records:", file->record_count);
|
|
|
|
|
|
|
|
if (file->record_length > 0)
|
|
|
|
printf("%-25s%"SC_FORMAT_LEN_SIZE_T"u bytes\n",
|
|
|
|
"Max. record size:", file->record_length);
|
2011-06-01 17:58:55 +00:00
|
|
|
|
|
|
|
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,
|
2020-02-02 13:42:22 +00:00
|
|
|
(15 > len) ? (15 - len) : 0, "",
|
2011-06-01 17:58:55 +00:00
|
|
|
util_acl_to_str(sc_file_get_acl_entry(file, ac_ops[i].id)));
|
|
|
|
}
|
|
|
|
|
2020-02-02 13:42:22 +00:00
|
|
|
if (file->type_attr_len > 0) {
|
|
|
|
printf("%-25s", "Type attributes:");
|
|
|
|
util_hex_dump(stdout, file->type_attr, file->type_attr_len, " ");
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
if (file->prop_attr_len > 0) {
|
2002-01-13 23:56:13 +00:00
|
|
|
printf("%-25s", "Proprietary attributes:");
|
2011-06-02 16:43:27 +00:00
|
|
|
util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, " ");
|
2002-01-13 23:56:13 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
2020-02-02 13:42:22 +00:00
|
|
|
if (file->sec_attr_len > 0) {
|
2002-02-11 15:55:34 +00:00
|
|
|
printf("%-25s", "Security attributes:");
|
2011-06-02 16:43:27 +00:00
|
|
|
util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, " ");
|
2002-02-11 15:55:34 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
2021-01-06 07:43:29 +00:00
|
|
|
for (i = 0; lc[i].str != NULL; i++)
|
|
|
|
if (file->status == lc[i].id)
|
|
|
|
lifecycle = lc[i].str;
|
|
|
|
printf("%-25s%s\n", "Life cycle: ", lifecycle);
|
2002-01-09 01:03:10 +00:00
|
|
|
printf("\n");
|
|
|
|
if (not_current) {
|
2002-02-20 09:56:47 +00:00
|
|
|
sc_file_free(file);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-03-09 00:04:44 +00:00
|
|
|
static int create_file(sc_file_t *file)
|
2002-01-16 23:59:18 +00:00
|
|
|
{
|
|
|
|
int r;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_create_file(card, file);
|
|
|
|
sc_unlock(card);
|
2002-01-16 23:59:18 +00:00
|
|
|
if (r) {
|
2002-02-20 09:56:47 +00:00
|
|
|
check_ret(r, SC_AC_OP_CREATE, "CREATE FILE failed", current_file);
|
2002-01-16 23:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Make sure we're back in the parent directory, because on some cards
|
|
|
|
* CREATE FILE also selects the newly created file. */
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2002-01-16 23:59:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_create(int argc, char **argv)
|
2002-01-10 23:02:48 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2002-01-21 10:56:30 +00:00
|
|
|
unsigned int size;
|
2002-11-07 14:48:03 +00:00
|
|
|
int r, op;
|
2002-01-10 23:02:48 +00:00
|
|
|
|
2013-04-13 18:56:41 +00:00
|
|
|
if (argc < 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_create);
|
2002-03-15 10:40:35 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 1) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_create);
|
2002-01-15 18:54:53 +00:00
|
|
|
/* %z isn't supported everywhere */
|
2008-05-05 13:00:01 +00:00
|
|
|
if (sscanf(argv[1], "%u", &size) != 1)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_create);
|
2013-04-13 18:56:41 +00:00
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
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;
|
2002-11-07 14:48:03 +00:00
|
|
|
for (op = 0; op < SC_MAX_AC_OPS; op++)
|
|
|
|
sc_file_add_acl_entry(file, op, SC_AC_NONE, 0);
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2013-04-13 18:56:41 +00:00
|
|
|
if (argc > 2) {
|
|
|
|
snprintf((char *)file->name, sizeof(file->name), "%s", argv[2]);
|
|
|
|
file->namelen = strlen((char *)file->name);
|
|
|
|
}
|
|
|
|
|
2002-02-20 09:56:47 +00:00
|
|
|
r = create_file(file);
|
|
|
|
sc_file_free(file);
|
|
|
|
return r;
|
2002-01-10 23:02:48 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_mkdir(int argc, char **argv)
|
2002-01-16 23:59:18 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2002-01-21 10:56:30 +00:00
|
|
|
unsigned int size;
|
2002-11-07 14:48:03 +00:00
|
|
|
int r, op;
|
2002-01-16 23:59:18 +00:00
|
|
|
|
2005-02-11 20:09:34 +00:00
|
|
|
if (argc != 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_mkdir);
|
2002-03-15 10:40:35 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 1) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_mkdir);
|
2008-05-05 13:00:01 +00:00
|
|
|
if (sscanf(argv[1], "%u", &size) != 1)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_mkdir);
|
2002-02-20 09:56:47 +00:00
|
|
|
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;
|
2002-11-07 14:48:03 +00:00
|
|
|
for (op = 0; op < SC_MAX_AC_OPS; op++)
|
|
|
|
sc_file_add_acl_entry(file, op, SC_AC_NONE, 0);
|
2002-02-20 09:56:47 +00:00
|
|
|
|
|
|
|
r = create_file(file);
|
|
|
|
sc_file_free(file);
|
|
|
|
return r;
|
2002-01-16 23:59:18 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_delete(int argc, char **argv)
|
2002-01-10 23:02:48 +00:00
|
|
|
{
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
2002-01-10 23:02:48 +00:00
|
|
|
int r;
|
|
|
|
|
2005-02-11 20:09:34 +00:00
|
|
|
if (argc != 1)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_delete);
|
2002-03-15 10:40:35 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 1) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_delete);
|
2002-02-20 09:56:47 +00:00
|
|
|
if (path.len != 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_delete);
|
2002-02-20 09:56:47 +00:00
|
|
|
path.type = SC_PATH_TYPE_FILE_ID;
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_delete_file(card, &path);
|
|
|
|
sc_unlock(card);
|
2002-01-10 23:02:48 +00:00
|
|
|
if (r) {
|
2002-02-20 09:56:47 +00:00
|
|
|
check_ret(r, SC_AC_OP_DELETE, "DELETE FILE failed", current_file);
|
2002-01-10 23:02:48 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-17 10:52:19 +00:00
|
|
|
static int do_pininfo(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;
|
|
|
|
size_t i;
|
|
|
|
struct sc_pin_cmd_data data;
|
|
|
|
int prefix_len = 0;
|
|
|
|
|
|
|
|
if (argc != 1)
|
|
|
|
return usage(do_pininfo);
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
data.cmd = SC_PIN_CMD_GET_INFO;
|
|
|
|
|
|
|
|
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) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid type.\n");
|
2018-06-17 10:52:19 +00:00
|
|
|
return usage(do_pininfo);
|
|
|
|
}
|
|
|
|
if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key reference.\n");
|
2018-06-17 10:52:19 +00:00
|
|
|
return usage(do_pininfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_pin_cmd(card, &data, &tries_left);
|
|
|
|
sc_unlock(card);
|
|
|
|
|
|
|
|
if (r) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Unable to get PIN info: %s\n", sc_strerror(r));
|
2018-06-17 10:52:19 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2019-01-01 11:33:16 +00:00
|
|
|
switch (data.pin1.logged_in) {
|
|
|
|
case SC_PIN_STATE_LOGGED_IN:
|
|
|
|
printf("Logged in.\n");
|
|
|
|
break;
|
|
|
|
case SC_PIN_STATE_LOGGED_OUT:
|
|
|
|
printf("Logged out.\n");
|
|
|
|
break;
|
|
|
|
case SC_PIN_STATE_UNKNOWN:
|
|
|
|
default:
|
2020-08-29 08:34:51 +00:00
|
|
|
printf("Login status unknown.\n");
|
2019-01-01 11:33:16 +00:00
|
|
|
}
|
|
|
|
if (tries_left >= 0)
|
|
|
|
printf("%d tries left.\n", tries_left);
|
2018-06-17 10:52:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_verify(int argc, char **argv)
|
2002-01-10 23:02:48 +00:00
|
|
|
{
|
2011-06-02 15:17:08 +00:00
|
|
|
const id2str_t typeNames[] = {
|
|
|
|
{ SC_AC_CHV, "CHV" },
|
|
|
|
{ SC_AC_AUT, "KEY" },
|
|
|
|
{ SC_AC_AUT, "AUT" },
|
|
|
|
{ SC_AC_PRO, "PRO" },
|
|
|
|
{ SC_AC_NONE, NULL, }
|
2002-01-10 23:02:48 +00:00
|
|
|
};
|
2005-02-04 18:10:23 +00:00
|
|
|
int r, tries_left = -1;
|
2020-02-22 16:50:12 +00:00
|
|
|
u8 buf[SC_MAX_PIN_SIZE];
|
2005-02-04 18:10:23 +00:00
|
|
|
size_t buflen = sizeof(buf), i;
|
2003-11-03 06:54:30 +00:00
|
|
|
struct sc_pin_cmd_data data;
|
2012-05-26 07:43:40 +00:00
|
|
|
int prefix_len = 0;
|
2003-11-03 06:54:30 +00:00
|
|
|
|
2002-03-15 10:40:35 +00:00
|
|
|
if (argc < 1 || argc > 2)
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_verify);
|
2003-11-03 06:54:30 +00:00
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
data.cmd = SC_PIN_CMD_VERIFY;
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
data.pin_type = SC_AC_NONE;
|
2011-06-02 15:17:08 +00:00
|
|
|
for (i = 0; typeNames[i].str; i++) {
|
2012-05-26 07:43:40 +00:00
|
|
|
prefix_len = strlen(typeNames[i].str);
|
|
|
|
if (strncasecmp(argv[0], typeNames[i].str, prefix_len) == 0) {
|
2011-06-02 15:17:08 +00:00
|
|
|
data.pin_type = typeNames[i].id;
|
2002-01-10 23:02:48 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-02-11 20:09:34 +00:00
|
|
|
}
|
2005-02-04 18:10:23 +00:00
|
|
|
if (data.pin_type == SC_AC_NONE) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid type.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_verify);
|
2002-01-10 23:02:48 +00:00
|
|
|
}
|
2012-05-26 07:43:40 +00:00
|
|
|
if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key reference.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_verify);
|
2002-01-10 23:02:48 +00:00
|
|
|
}
|
2003-11-03 06:54:30 +00:00
|
|
|
|
2005-02-11 20:09:34 +00:00
|
|
|
if (argc < 2) {
|
2012-06-17 11:05:52 +00:00
|
|
|
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) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "util_getpass error.\n");
|
2012-06-17 11:05:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-08-02 20:01:51 +00:00
|
|
|
|
|
|
|
if (strlcpy((char *)buf, pin, sizeof(buf)) >= sizeof(buf)) {
|
2012-06-17 11:05:52 +00:00
|
|
|
free(pin);
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "PIN too long - aborting VERIFY.\n");
|
2012-06-17 11:05:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
free(pin);
|
|
|
|
data.pin1.data = buf;
|
2013-08-02 20:01:51 +00:00
|
|
|
data.pin1.len = strlen((char *)buf);
|
2003-11-03 06:54:30 +00:00
|
|
|
}
|
2003-11-26 15:49:09 +00:00
|
|
|
} else {
|
2011-06-02 13:39:12 +00:00
|
|
|
r = parse_string_or_hexdata(argv[1], buf, &buflen);
|
2003-11-26 15:49:09 +00:00
|
|
|
if (0 != r) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key value.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_verify);
|
2003-11-20 16:01:56 +00:00
|
|
|
}
|
|
|
|
data.pin1.data = buf;
|
|
|
|
data.pin1.len = buflen;
|
2002-01-10 23:02:48 +00:00
|
|
|
}
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_pin_cmd(card, &data, &tries_left);
|
|
|
|
sc_unlock(card);
|
2003-11-03 06:54:30 +00:00
|
|
|
|
2002-01-10 23:02:48 +00:00
|
|
|
if (r) {
|
|
|
|
if (r == SC_ERROR_PIN_CODE_INCORRECT) {
|
2012-04-02 22:00:56 +00:00
|
|
|
if (tries_left >= 0)
|
2002-01-20 21:20:09 +00:00
|
|
|
printf("Incorrect code, %d tries left.\n", tries_left);
|
2002-01-10 23:02:48 +00:00
|
|
|
else
|
|
|
|
printf("Incorrect code.\n");
|
2002-04-04 20:37:27 +00:00
|
|
|
} else
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Unable to verify PIN code: %s\n", sc_strerror(r));
|
2002-01-10 23:02:48 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
printf("Code correct.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_change(int argc, char **argv)
|
2002-03-15 10:40:35 +00:00
|
|
|
{
|
2005-02-04 18:10:23 +00:00
|
|
|
int ref, r, tries_left = -1;
|
2020-02-22 16:50:12 +00:00
|
|
|
u8 oldpin[SC_MAX_PIN_SIZE];
|
|
|
|
u8 newpin[SC_MAX_PIN_SIZE];
|
2011-06-02 14:40:24 +00:00
|
|
|
size_t oldpinlen = 0;
|
|
|
|
size_t newpinlen = 0;
|
2012-06-17 14:48:21 +00:00
|
|
|
struct sc_pin_cmd_data data;
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
data.cmd = SC_PIN_CMD_CHANGE;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2009-12-03 11:11:04 +00:00
|
|
|
if (argc < 1 || argc > 3)
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_change);
|
2005-01-21 18:47:41 +00:00
|
|
|
if (strncasecmp(argv[0], "CHV", 3)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid type.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_change);
|
2002-03-15 10:40:35 +00:00
|
|
|
}
|
|
|
|
if (sscanf(argv[0] + 3, "%d", &ref) != 1) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key reference.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_change);
|
2002-03-15 10:40:35 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 14:40:24 +00:00
|
|
|
if (argc == 3) {
|
|
|
|
oldpinlen = sizeof(oldpin);
|
|
|
|
if (parse_string_or_hexdata(argv[1], oldpin, &oldpinlen) != 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key value.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_change);
|
2003-10-21 08:59:11 +00:00
|
|
|
}
|
2002-03-15 10:40:35 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 14:40:24 +00:00
|
|
|
if (argc >= 2) {
|
|
|
|
newpinlen = sizeof(newpin);
|
|
|
|
if (parse_string_or_hexdata(argv[argc-1], newpin, &newpinlen) != 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key value.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_change);
|
2009-12-03 11:11:04 +00:00
|
|
|
}
|
2002-03-15 10:40:35 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 14:48:21 +00:00
|
|
|
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;
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_pin_cmd(card, &data, &tries_left);
|
|
|
|
sc_unlock(card);
|
2002-03-15 10:40:35 +00:00
|
|
|
if (r) {
|
|
|
|
if (r == SC_ERROR_PIN_CODE_INCORRECT) {
|
2012-04-02 22:00:56 +00:00
|
|
|
if (tries_left >= 0)
|
2002-03-15 10:40:35 +00:00
|
|
|
printf("Incorrect code, %d tries left.\n", tries_left);
|
|
|
|
else
|
|
|
|
printf("Incorrect code.\n");
|
|
|
|
}
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Unable to change PIN code: %s\n", sc_strerror(r));
|
2002-03-15 10:40:35 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
printf("PIN changed.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-03 11:11:04 +00:00
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_unblock(int argc, char **argv)
|
2002-12-19 19:42:51 +00:00
|
|
|
{
|
2005-02-04 18:10:23 +00:00
|
|
|
int ref, r;
|
2020-02-22 16:50:12 +00:00
|
|
|
u8 puk[SC_MAX_PIN_SIZE];
|
|
|
|
u8 newpin[SC_MAX_PIN_SIZE];
|
2011-06-02 14:40:24 +00:00
|
|
|
size_t puklen = 0;
|
|
|
|
size_t newpinlen = 0;
|
2012-06-17 14:48:21 +00:00
|
|
|
struct sc_pin_cmd_data data;
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
data.cmd = SC_PIN_CMD_UNBLOCK;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2009-12-03 11:11:04 +00:00
|
|
|
if (argc < 1 || argc > 3)
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_unblock);
|
2005-01-21 18:47:41 +00:00
|
|
|
if (strncasecmp(argv[0], "CHV", 3)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid type.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_unblock);
|
2002-12-19 19:42:51 +00:00
|
|
|
}
|
|
|
|
if (sscanf(argv[0] + 3, "%d", &ref) != 1) {
|
|
|
|
printf("Invalid key reference.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_unblock);
|
2002-12-19 19:42:51 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 14:40:24 +00:00
|
|
|
if (argc > 1) {
|
|
|
|
puklen = sizeof(puk);
|
|
|
|
if (parse_string_or_hexdata(argv[1], puk, &puklen) != 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key value.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_unblock);
|
2003-10-21 08:59:11 +00:00
|
|
|
}
|
2002-12-19 19:42:51 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 14:40:24 +00:00
|
|
|
if (argc > 2) {
|
|
|
|
newpinlen = sizeof(newpin);
|
|
|
|
if (parse_string_or_hexdata(argv[2], newpin, &newpinlen) != 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid key value.\n");
|
2012-06-17 16:46:08 +00:00
|
|
|
return usage(do_unblock);
|
2009-12-03 11:11:04 +00:00
|
|
|
}
|
2002-12-19 19:42:51 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 14:48:21 +00:00
|
|
|
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;
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_pin_cmd(card, &data, NULL);
|
|
|
|
sc_unlock(card);
|
2002-12-19 19:42:51 +00:00
|
|
|
if (r) {
|
|
|
|
if (r == SC_ERROR_PIN_CODE_INCORRECT)
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Incorrect code.\n");
|
|
|
|
fprintf(stderr, "Unable to unblock PIN code: %s\n", sc_strerror(r));
|
2002-12-19 19:42:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
printf("PIN unblocked.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_get(int argc, char **argv)
|
2002-01-13 23:56:13 +00:00
|
|
|
{
|
2020-02-08 17:05:14 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_RESP_SIZE];
|
2008-05-26 10:46:16 +00:00
|
|
|
int r, err = 1;
|
2002-01-13 23:56:13 +00:00
|
|
|
size_t count = 0;
|
2005-01-21 18:47:41 +00:00
|
|
|
unsigned int idx = 0;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
2008-05-26 10:46:16 +00:00
|
|
|
sc_file_t *file = NULL;
|
2011-06-02 17:47:50 +00:00
|
|
|
char *filename;
|
2002-01-13 23:56:13 +00:00
|
|
|
FILE *outf = NULL;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2005-01-21 18:47:41 +00:00
|
|
|
if (argc < 1 || argc > 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_get);
|
2002-03-15 10:40:35 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_get);
|
2003-09-25 09:33:16 +00:00
|
|
|
|
2020-02-02 11:57:08 +00:00
|
|
|
filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_', 0);
|
2011-03-31 07:44:59 +00:00
|
|
|
outf = (strcmp(filename, "-") == 0)
|
|
|
|
? stdout
|
|
|
|
: fopen(filename, "wb");
|
2002-01-13 23:56:13 +00:00
|
|
|
if (outf == NULL) {
|
|
|
|
perror(filename);
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (outf == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_BINARY);
|
|
|
|
#endif
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2018-09-27 12:44:39 +00:00
|
|
|
if (r || file == NULL) {
|
2020-02-08 17:05:14 +00:00
|
|
|
check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file);
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2019-10-14 15:35:17 +00:00
|
|
|
if (file->type != SC_FILE_TYPE_WORKING_EF || file->ef_structure != SC_FILE_EF_TRANSPARENT) {
|
2020-02-08 17:05:14 +00:00
|
|
|
fprintf(stderr, "Only transparent working EFs may be read\n");
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2002-02-26 11:23:25 +00:00
|
|
|
}
|
2002-02-20 09:56:47 +00:00
|
|
|
count = file->size;
|
2002-01-13 23:56:13 +00:00
|
|
|
while (count) {
|
2013-09-18 16:13:15 +00:00
|
|
|
/* FIXME sc_read_binary does this kind of fetching in a loop already */
|
2002-01-13 23:56:13 +00:00
|
|
|
int c = count > sizeof(buf) ? sizeof(buf) : count;
|
|
|
|
|
|
|
|
r = sc_read_binary(card, idx, buf, c, 0);
|
|
|
|
if (r < 0) {
|
2020-02-08 17:05:14 +00:00
|
|
|
check_ret(r, SC_AC_OP_READ, "Read failed", file);
|
2005-01-21 18:47:41 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2011-05-27 12:33:52 +00:00
|
|
|
if ((r != c) && (card->type != SC_CARD_TYPE_BELPIC_EID)) {
|
2020-02-08 17:05:14 +00:00
|
|
|
fprintf(stderr, "Expecting %d, got only %d bytes.\n", c, r);
|
2005-01-21 18:47:41 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2011-05-27 12:33:52 +00:00
|
|
|
if ((r == 0) && (card->type == SC_CARD_TYPE_BELPIC_EID))
|
2005-02-01 07:52:40 +00:00
|
|
|
break;
|
|
|
|
fwrite(buf, r, 1, outf);
|
|
|
|
idx += r;
|
|
|
|
count -= r;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2011-03-31 07:44:59 +00:00
|
|
|
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);
|
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2008-05-26 10:46:16 +00:00
|
|
|
err = 0;
|
2002-01-13 23:56:13 +00:00
|
|
|
err:
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(file);
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (outf == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_TEXT);
|
|
|
|
#endif
|
2011-03-31 07:44:59 +00:00
|
|
|
if (outf != NULL && outf != stdout)
|
2008-05-26 10:46:16 +00:00
|
|
|
fclose(outf);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2005-02-04 18:10:23 +00:00
|
|
|
return -err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 09:40:05 +00:00
|
|
|
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;
|
|
|
|
}
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (outf == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_BINARY);
|
|
|
|
#endif
|
2020-01-12 09:40:05 +00:00
|
|
|
|
|
|
|
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);
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (outf == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_TEXT);
|
|
|
|
#endif
|
2020-01-12 09:40:05 +00:00
|
|
|
if (outf != NULL && outf != stdout)
|
|
|
|
fclose(outf);
|
|
|
|
select_current_path_or_die();
|
|
|
|
return -err;
|
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_update_binary(int argc, char **argv)
|
2004-06-24 17:25:23 +00:00
|
|
|
{
|
2020-02-08 15:15:27 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_DATA_SIZE];
|
2011-06-02 13:39:12 +00:00
|
|
|
size_t buflen = sizeof(buf);
|
|
|
|
int r, err = 1;
|
2004-06-24 17:25:23 +00:00
|
|
|
int offs;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2011-06-02 13:51:30 +00:00
|
|
|
if (argc != 3)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_update_binary);
|
2004-06-24 17:25:23 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_update_binary);
|
2020-02-08 15:15:27 +00:00
|
|
|
offs = strtol(argv[1], NULL, 10);
|
2011-06-02 13:39:12 +00:00
|
|
|
|
2011-06-02 14:19:38 +00:00
|
|
|
r = parse_string_or_hexdata(argv[2], buf, &buflen);
|
2011-06-02 13:39:12 +00:00
|
|
|
if (r < 0) {
|
2020-03-21 15:47:26 +00:00
|
|
|
fprintf(stderr, "Unable to parse input data: %s\n", sc_strerror(r));
|
2011-06-02 13:39:12 +00:00
|
|
|
return -1;
|
2004-06-24 17:25:23 +00:00
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2004-06-24 17:25:23 +00:00
|
|
|
if (r) {
|
2020-02-08 15:15:27 +00:00
|
|
|
check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file);
|
2004-06-24 17:25:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-02-08 15:15:27 +00:00
|
|
|
if (file->ef_structure != SC_FILE_EF_TRANSPARENT) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "EF structure should be SC_FILE_EF_TRANSPARENT\n");
|
2004-06-24 17:25:23 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_update_binary(card, offs, buf, buflen, 0);
|
|
|
|
sc_unlock(card);
|
2004-06-24 17:25:23 +00:00
|
|
|
if (r < 0) {
|
2020-03-21 15:47:26 +00:00
|
|
|
fprintf(stderr, "Cannot update %04X: %s\n", file->id, sc_strerror(r));
|
2004-06-24 17:25:23 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-02-08 15:15:27 +00:00
|
|
|
printf("Total of %d bytes written to %04X at offset %d.\n",
|
2005-01-21 18:47:41 +00:00
|
|
|
r, file->id, offs);
|
2008-05-26 10:46:16 +00:00
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
err = 0;
|
2004-06-24 17:25:23 +00:00
|
|
|
err:
|
|
|
|
sc_file_free(file);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2005-02-04 18:10:23 +00:00
|
|
|
return -err;
|
2004-06-24 17:25:23 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_update_record(int argc, char **argv)
|
2004-06-24 17:25:23 +00:00
|
|
|
{
|
2020-02-08 15:35:08 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_DATA_SIZE];
|
2011-06-02 14:02:17 +00:00
|
|
|
size_t buflen;
|
2020-02-08 15:35:08 +00:00
|
|
|
int r, err = 1;
|
2019-01-14 09:35:00 +00:00
|
|
|
size_t rec, offs;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2011-06-02 13:51:30 +00:00
|
|
|
if (argc != 4)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_update_record);
|
2004-06-24 17:25:23 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_update_record);
|
2020-02-08 15:35:08 +00:00
|
|
|
rec = strtol(argv[1], NULL, 10);
|
|
|
|
offs = strtol(argv[2], NULL, 10);
|
2004-06-24 17:25:23 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2004-06-24 17:25:23 +00:00
|
|
|
if (r) {
|
2020-02-08 15:35:08 +00:00
|
|
|
check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file);
|
2004-06-24 17:25:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-02-08 15:35:08 +00:00
|
|
|
if (file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "EF structure should be SC_FILE_EF_LINEAR_VARIABLE\n");
|
2004-06-24 17:25:23 +00:00
|
|
|
goto err;
|
2020-02-08 15:35:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rec < 1 || rec > file->record_count) {
|
2019-01-14 09:35:00 +00:00
|
|
|
fprintf(stderr, "Invalid record number %"SC_FORMAT_LEN_SIZE_T"u\n", rec);
|
2004-06-24 17:25:23 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2004-06-24 17:25:23 +00:00
|
|
|
r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR);
|
2020-02-08 15:35:08 +00:00
|
|
|
if (r < 0) {
|
|
|
|
fprintf(stderr, "Cannot read record %"SC_FORMAT_LEN_SIZE_T"u of %04X: %s\n",
|
2020-03-21 15:47:26 +00:00
|
|
|
rec, file->id, sc_strerror(r));
|
2020-02-08 15:35:08 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do not allow gaps between data read and added */
|
|
|
|
if (offs >= (size_t) r) {
|
|
|
|
fprintf(stderr, "Offset too large.\n");
|
|
|
|
goto err;
|
2004-06-24 17:25:23 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 14:02:17 +00:00
|
|
|
buflen = sizeof(buf) - offs;
|
2020-02-08 15:35:08 +00:00
|
|
|
r = parse_string_or_hexdata(argv[3], buf + offs, &buflen);
|
|
|
|
if (r < 0) {
|
2020-03-21 15:47:26 +00:00
|
|
|
fprintf(stderr, "Unable to parse input data: %s.\n", sc_strerror(r));
|
2004-06-24 17:25:23 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
2020-02-08 15:35:08 +00:00
|
|
|
r = sc_update_record(card, rec, buf, buflen, SC_RECORD_BY_REC_NR);
|
2018-05-18 21:21:14 +00:00
|
|
|
sc_unlock(card);
|
2020-02-08 15:35:08 +00:00
|
|
|
if (r < 0) {
|
|
|
|
fprintf(stderr, "Cannot update record %"SC_FORMAT_LEN_SIZE_T"u of %04X: %s\n.",
|
2020-03-21 15:47:26 +00:00
|
|
|
rec, file->id, sc_strerror(r));
|
2004-06-24 17:25:23 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-02-08 15:35:08 +00:00
|
|
|
printf("Total of %d bytes written to %04X's record %"SC_FORMAT_LEN_SIZE_T"u "
|
|
|
|
"at offset %"SC_FORMAT_LEN_SIZE_T"u.\n",
|
|
|
|
r, file->id, rec, offs);
|
2008-05-26 10:46:16 +00:00
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
err = 0;
|
2004-06-24 17:25:23 +00:00
|
|
|
err:
|
|
|
|
sc_file_free(file);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2005-02-04 18:10:23 +00:00
|
|
|
return -err;
|
2004-06-24 17:25:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_put(int argc, char **argv)
|
2002-01-13 23:56:13 +00:00
|
|
|
{
|
2020-02-08 18:39:18 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_DATA_SIZE];
|
2008-05-26 10:46:16 +00:00
|
|
|
int r, err = 1;
|
2002-01-13 23:56:13 +00:00
|
|
|
size_t count = 0;
|
2005-01-21 18:47:41 +00:00
|
|
|
unsigned int idx = 0;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_path_t path;
|
2008-05-26 10:46:16 +00:00
|
|
|
sc_file_t *file = NULL;
|
2002-01-13 23:56:13 +00:00
|
|
|
const char *filename;
|
2018-07-08 07:34:06 +00:00
|
|
|
FILE *inf = NULL;
|
2005-01-21 18:47:41 +00:00
|
|
|
|
|
|
|
if (argc < 1 || argc > 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_put);
|
2002-03-15 10:40:35 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_put);
|
2011-06-02 17:47:50 +00:00
|
|
|
|
2020-02-02 11:57:08 +00:00
|
|
|
filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_', 0);
|
2018-07-08 07:34:06 +00:00
|
|
|
inf = fopen(filename, "rb");
|
|
|
|
if (inf == NULL) {
|
2002-01-13 23:56:13 +00:00
|
|
|
perror(filename);
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2018-09-27 12:44:39 +00:00
|
|
|
if (r || file == NULL) {
|
2020-02-08 18:39:18 +00:00
|
|
|
check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file);
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
2002-02-20 09:56:47 +00:00
|
|
|
count = file->size;
|
2002-01-13 23:56:13 +00:00
|
|
|
while (count) {
|
|
|
|
int c = count > sizeof(buf) ? sizeof(buf) : count;
|
|
|
|
|
2018-07-08 07:34:06 +00:00
|
|
|
r = fread(buf, 1, c, inf);
|
2002-01-13 23:56:13 +00:00
|
|
|
if (r < 0) {
|
|
|
|
perror("fread");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (r != c)
|
|
|
|
count = c = r;
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_update_binary(card, idx, buf, c, 0);
|
|
|
|
sc_unlock(card);
|
2002-01-13 23:56:13 +00:00
|
|
|
if (r < 0) {
|
2020-02-08 18:39:18 +00:00
|
|
|
check_ret(r, SC_AC_OP_READ, "Update failed", file);
|
2005-01-21 18:47:41 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
|
|
|
if (r != c) {
|
2020-02-08 18:39:18 +00:00
|
|
|
fprintf(stderr, "Expecting %d, wrote only %d bytes.\n", c, r);
|
2005-01-21 18:47:41 +00:00
|
|
|
goto err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
|
|
|
idx += c;
|
|
|
|
count -= c;
|
|
|
|
}
|
2020-02-08 18:39:18 +00:00
|
|
|
printf("Total of %d bytes written to %04X.\n", idx, file->id);
|
2008-05-26 10:46:16 +00:00
|
|
|
|
|
|
|
err = 0;
|
2002-01-13 23:56:13 +00:00
|
|
|
err:
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(file);
|
2018-07-08 07:34:06 +00:00
|
|
|
if (inf)
|
|
|
|
fclose(inf);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2005-02-04 18:10:23 +00:00
|
|
|
return -err;
|
2002-01-13 23:56:13 +00:00
|
|
|
}
|
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_debug(int argc, char **argv)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2005-01-21 18:47:41 +00:00
|
|
|
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;
|
2011-04-18 10:01:27 +00:00
|
|
|
if (i > 1) {
|
|
|
|
sc_ctx_log_to_file(ctx, "stderr");
|
2005-01-21 18:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2002-03-15 10:40:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
static int do_erase(int argc, char **argv)
|
2002-04-05 15:04:35 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (argc != 0)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_erase);
|
2002-03-15 10:40:35 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL);
|
|
|
|
sc_unlock(card);
|
2002-04-05 15:04:35 +00:00
|
|
|
if (r) {
|
2020-03-21 15:47:26 +00:00
|
|
|
fprintf(stderr, "Failed to erase card: %s\n", sc_strerror(r));
|
2002-04-05 15:04:35 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2002-03-15 10:40:35 +00:00
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
static int do_random(int argc, char **argv)
|
2003-01-15 13:20:22 +00:00
|
|
|
{
|
2017-11-24 13:13:55 +00:00
|
|
|
unsigned char buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2005-01-21 18:47:41 +00:00
|
|
|
int r, count;
|
2018-07-08 08:40:06 +00:00
|
|
|
const char *filename = NULL;
|
|
|
|
FILE *outf = NULL;
|
2003-01-15 13:20:22 +00:00
|
|
|
|
2018-07-08 08:40:06 +00:00
|
|
|
if (argc < 1 || argc > 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_random);
|
2003-01-15 13:20:22 +00:00
|
|
|
count = atoi(argv[0]);
|
2020-02-08 18:12:21 +00:00
|
|
|
|
|
|
|
if (count < 0 || (size_t) count > sizeof(buffer)) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Number must be in range 0..%"SC_FORMAT_LEN_SIZE_T"u\n",
|
2020-02-08 18:12:21 +00:00
|
|
|
sizeof(buffer));
|
2003-01-15 13:20:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-07-08 08:40:06 +00:00
|
|
|
if (argc == 2) {
|
|
|
|
filename = argv[1];
|
|
|
|
|
|
|
|
if (interactive && strcmp(filename, "-") == 0) {
|
|
|
|
fprintf(stderr, "Binary writing to stdout not supported in interactive mode\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
outf = (strcmp(filename, "-") == 0)
|
|
|
|
? stdout
|
|
|
|
: fopen(filename, "wb");
|
|
|
|
if (outf == NULL) {
|
|
|
|
perror(filename);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (outf == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_BINARY);
|
|
|
|
#endif
|
2018-07-08 08:40:06 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_get_challenge(card, buffer, count);
|
|
|
|
sc_unlock(card);
|
2003-01-15 13:20:22 +00:00
|
|
|
if (r < 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Failed to get random bytes: %s\n", sc_strerror(r));
|
2018-11-06 14:45:51 +00:00
|
|
|
if (argc == 2) {
|
|
|
|
fclose(outf);
|
|
|
|
}
|
2003-01-15 13:20:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-07-08 08:40:06 +00:00
|
|
|
if (argc == 2) {
|
|
|
|
/* outf is guaranteed to be non-NULL */
|
|
|
|
size_t written = fwrite(buffer, 1, count, outf);
|
|
|
|
|
|
|
|
if (written < (size_t) count)
|
|
|
|
perror(filename);
|
2020-03-14 14:48:37 +00:00
|
|
|
|
2018-07-08 08:40:06 +00:00
|
|
|
if (outf == stdout) {
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
_setmode(fileno(stdout), _O_TEXT);
|
|
|
|
#endif
|
2018-07-08 08:40:06 +00:00
|
|
|
printf("\nTotal of %"SC_FORMAT_LEN_SIZE_T"u random bytes written\n", written);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
printf("Total of %"SC_FORMAT_LEN_SIZE_T"u random bytes written to %s\n",
|
|
|
|
written, filename);
|
|
|
|
|
|
|
|
fclose(outf);
|
|
|
|
|
|
|
|
if (written < (size_t) count)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
util_hex_dump_asc(stdout, buffer, count, 0);
|
|
|
|
}
|
2003-01-15 13:20:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2002-03-15 10:40:35 +00:00
|
|
|
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_get_data(int argc, char **argv)
|
2003-10-30 17:04:50 +00:00
|
|
|
{
|
2020-02-08 13:34:46 +00:00
|
|
|
u8 id[2] = { 0x00, 0x00 };
|
2005-01-21 18:47:41 +00:00
|
|
|
unsigned int tag;
|
2020-02-08 13:34:46 +00:00
|
|
|
u8 buffer[SC_MAX_EXT_APDU_RESP_SIZE];
|
2005-01-21 18:47:41 +00:00
|
|
|
FILE *fp;
|
|
|
|
int r;
|
2003-10-30 17:04:50 +00:00
|
|
|
|
|
|
|
if (argc != 1 && argc != 2)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_get_data);
|
2020-02-08 13:34:46 +00:00
|
|
|
if (arg_to_fid(argv[0], id) != 0)
|
|
|
|
return usage(do_get_data);
|
|
|
|
tag = id[0] << 8 | id[1];
|
2003-10-30 17:04:50 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_get_data(card, tag, buffer, sizeof(buffer));
|
|
|
|
sc_unlock(card);
|
2003-10-30 17:04:50 +00:00
|
|
|
if (r < 0) {
|
2020-02-08 13:34:46 +00:00
|
|
|
fprintf(stderr, "Failed to get DO %04X: %s\n", tag, sc_strerror(r));
|
2003-10-30 17:04:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 2) {
|
2020-02-08 13:34:46 +00:00
|
|
|
const char *filename = argv[1];
|
2003-10-30 17:04:50 +00:00
|
|
|
|
2020-03-14 14:48:37 +00:00
|
|
|
fp = (strcmp(filename, "-") == 0)
|
|
|
|
? stdout
|
|
|
|
: fopen(filename, "wb");
|
|
|
|
if (fp == NULL) {
|
2003-10-30 17:04:50 +00:00
|
|
|
perror(filename);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (fp == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_BINARY);
|
|
|
|
#endif
|
2003-10-30 17:04:50 +00:00
|
|
|
fwrite(buffer, r, 1, fp);
|
2020-03-14 14:48:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (fp == stdout)
|
|
|
|
_setmode(fileno(stdout), _O_TEXT);
|
|
|
|
#endif
|
|
|
|
if (fp != stdout)
|
|
|
|
fclose(fp);
|
2003-10-30 17:04:50 +00:00
|
|
|
} else {
|
2020-02-08 13:34:46 +00:00
|
|
|
printf("Data Object %04X:\n", tag & 0xFFFF);
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, buffer, r, 0);
|
2003-10-30 17:04:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-01 15:41:49 +00:00
|
|
|
/**
|
|
|
|
* Use PUT DATA command to write to Data Object.
|
|
|
|
**/
|
2005-02-04 18:10:23 +00:00
|
|
|
static int do_put_data(int argc, char **argv)
|
2003-10-30 17:04:50 +00:00
|
|
|
{
|
2020-02-08 14:14:01 +00:00
|
|
|
u8 id[2] = { 0x00, 0x00 };
|
2012-06-01 15:41:49 +00:00
|
|
|
unsigned int tag;
|
2020-02-08 14:14:01 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_DATA_SIZE];
|
2012-06-01 15:41:49 +00:00
|
|
|
size_t buflen = sizeof(buf);
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (argc != 2)
|
|
|
|
return usage(do_put_data);
|
|
|
|
|
2020-02-08 14:14:01 +00:00
|
|
|
if (arg_to_fid(argv[0], id) != 0)
|
|
|
|
return usage(do_get_data);
|
|
|
|
tag = id[0] << 8 | id[1];
|
2012-06-01 15:41:49 +00:00
|
|
|
|
|
|
|
/* Extract the new content */
|
|
|
|
/* buflen is the max length of reception buffer */
|
|
|
|
r = parse_string_or_hexdata(argv[1], buf, &buflen);
|
|
|
|
if (r < 0) {
|
2020-02-08 14:14:01 +00:00
|
|
|
fprintf(stderr, "Error parsing %s: %s\n", argv[1], sc_strerror(r));
|
2018-07-07 20:20:07 +00:00
|
|
|
return r;
|
2012-06-01 15:41:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call OpenSC to do put data */
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_put_data(card, tag, buf, buflen);
|
|
|
|
sc_unlock(card);
|
2012-06-01 15:41:49 +00:00
|
|
|
if (r < 0) {
|
2020-02-08 14:14:01 +00:00
|
|
|
fprintf(stderr, "Failed to put data to DO %04X: %s\n", tag, sc_strerror(r));
|
2012-06-01 15:41:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Total of %d bytes written.\n", r);
|
|
|
|
|
|
|
|
return 0;
|
2003-10-30 17:04:50 +00:00
|
|
|
}
|
|
|
|
|
2007-09-27 18:19:17 +00:00
|
|
|
static int do_apdu(int argc, char **argv)
|
|
|
|
{
|
|
|
|
sc_apdu_t apdu;
|
2018-07-07 20:27:22 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2017-11-24 13:13:55 +00:00
|
|
|
u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2011-06-15 18:53:50 +00:00
|
|
|
size_t len, i;
|
|
|
|
int r;
|
2007-09-27 18:19:17 +00:00
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
if (argc < 1)
|
|
|
|
return usage(do_apdu);
|
2007-09-27 18:19:17 +00:00
|
|
|
|
2018-07-07 20:27:22 +00:00
|
|
|
/* loop over the args and parse them, making sure the result fits into buf[] */
|
2019-06-20 16:33:22 +00:00
|
|
|
for (i = 0, len = 0; i < (unsigned) argc && len < sizeof(buf); i++) {
|
2018-07-07 20:27:22 +00:00
|
|
|
size_t len0 = sizeof(buf) - len;
|
2011-06-02 15:46:30 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
2010-07-01 12:31:52 +00:00
|
|
|
len += len0;
|
|
|
|
}
|
|
|
|
|
2011-04-26 07:29:53 +00:00
|
|
|
r = sc_bytes2apdu(card->ctx, buf, len, &apdu);
|
|
|
|
if (r) {
|
|
|
|
fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(r));
|
|
|
|
return 2;
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
2011-04-19 10:51:22 +00:00
|
|
|
|
2007-09-27 18:19:17 +00:00
|
|
|
apdu.resp = rbuf;
|
|
|
|
apdu.resplen = sizeof(rbuf);
|
|
|
|
|
|
|
|
printf("Sending: ");
|
2011-06-02 15:46:30 +00:00
|
|
|
util_hex_dump(stdout, buf, len, " ");
|
2007-09-27 18:19:17 +00:00
|
|
|
printf("\n");
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_transmit_apdu(card, &apdu);
|
|
|
|
sc_unlock(card);
|
2007-09-27 18:19:17 +00:00
|
|
|
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)
|
2008-03-06 16:06:59 +00:00
|
|
|
util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
|
2007-09-27 18:19:17 +00:00
|
|
|
|
2012-06-17 19:17:09 +00:00
|
|
|
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
|
if (r)
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Failure: %s\n", sc_strerror(r));
|
2012-06-17 19:17:09 +00:00
|
|
|
else
|
|
|
|
printf("Success!\n");
|
2007-09-27 18:19:17 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_asn1(int argc, char **argv)
|
|
|
|
{
|
2008-05-26 10:46:16 +00:00
|
|
|
int r, err = 1;
|
2020-05-21 08:50:53 +00:00
|
|
|
unsigned int offs = 0;
|
2021-02-08 19:59:43 +00:00
|
|
|
int offsu = 0; /* offset updated, set from argv */
|
2007-09-27 18:19:17 +00:00
|
|
|
sc_path_t path;
|
2008-05-26 10:46:16 +00:00
|
|
|
sc_file_t *file = NULL;
|
2007-09-27 18:19:17 +00:00
|
|
|
int not_current = 1;
|
2020-03-15 17:30:37 +00:00
|
|
|
u8 buf[SC_MAX_EXT_APDU_DATA_SIZE];
|
2007-09-27 18:19:17 +00:00
|
|
|
|
2020-05-21 08:50:53 +00:00
|
|
|
if (argc > 3)
|
2011-06-02 11:21:25 +00:00
|
|
|
return usage(do_asn1);
|
2007-09-27 18:19:17 +00:00
|
|
|
|
2008-05-26 10:46:16 +00:00
|
|
|
/* select file */
|
2007-09-27 18:19:17 +00:00
|
|
|
if (argc) {
|
2011-05-03 05:39:24 +00:00
|
|
|
if (arg_to_path(argv[0], &path, 0) != 0) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Invalid file path\n");
|
2007-09-27 18:19:17 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, &path, &file);
|
|
|
|
sc_unlock(card);
|
2007-09-27 18:19:17 +00:00
|
|
|
if (r) {
|
2020-03-15 17:30:37 +00:00
|
|
|
check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file);
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
path = current_path;
|
|
|
|
file = current_file;
|
|
|
|
not_current = 0;
|
|
|
|
}
|
|
|
|
if (file->type != SC_FILE_TYPE_WORKING_EF) {
|
2020-03-15 17:30:37 +00:00
|
|
|
fprintf(stderr, "Only working EFs may be read\n");
|
2008-05-26 10:46:16 +00:00
|
|
|
goto err;
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
|
|
|
|
2008-05-26 10:46:16 +00:00
|
|
|
/* read */
|
2020-03-15 17:30:37 +00:00
|
|
|
if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
|
|
|
|
size_t size = (file->size > 0) ? file->size : sizeof(buf);
|
|
|
|
|
2020-05-21 08:50:53 +00:00
|
|
|
if (argc > 2) {
|
|
|
|
fprintf(stderr, "Transparent EFs do not have records\n");
|
2020-03-15 17:30:37 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2021-02-08 19:59:43 +00:00
|
|
|
if (argc > 1) {
|
2020-05-21 08:50:53 +00:00
|
|
|
offs = (unsigned int) strtoul(argv[1], NULL, 10);
|
2021-02-08 19:59:43 +00:00
|
|
|
offsu = 1;
|
|
|
|
}
|
2020-05-21 08:50:53 +00:00
|
|
|
|
2020-03-15 17:30:37 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_read_binary(card, 0, buf, MIN(size, sizeof(buf)), 0);
|
|
|
|
sc_unlock(card);
|
|
|
|
if (r < 0) {
|
|
|
|
check_ret(r, SC_AC_OP_READ, "read failed", file);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if ((size_t) r != file->size) {
|
|
|
|
fprintf(stderr, "WARNING: expecting %"SC_FORMAT_LEN_SIZE_T"u, got %d bytes.\n",
|
|
|
|
file->size, r);
|
2020-05-21 08:50:53 +00:00
|
|
|
/* some cards return a bogus value for file length.
|
|
|
|
* As long as the actual length is not higher
|
|
|
|
* than the expected length, continue */
|
2020-03-15 17:30:37 +00:00
|
|
|
if ((size_t) r > file->size)
|
|
|
|
goto err;
|
|
|
|
}
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
2020-03-15 17:30:37 +00:00
|
|
|
else { /* record-oriented file */
|
|
|
|
unsigned int rec = 0;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
fprintf(stderr, "Record-oriented EFs require parameter record number.\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
rec = (unsigned int) strtoul(argv[1], NULL, 10);
|
2021-02-08 19:59:43 +00:00
|
|
|
if (argc > 2) {
|
2020-05-21 08:50:53 +00:00
|
|
|
offs = (unsigned int) strtoul(argv[2], NULL, 10);
|
2021-02-08 19:59:43 +00:00
|
|
|
offsu = 1;
|
|
|
|
}
|
2020-05-21 08:50:53 +00:00
|
|
|
|
2020-03-15 17:30:37 +00:00
|
|
|
if (rec < 1 || rec > file->record_count) {
|
|
|
|
fprintf(stderr, "Invalid record number %u.\n", rec);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR);
|
|
|
|
else
|
|
|
|
r = SC_ERROR_READER_LOCKED;
|
|
|
|
sc_unlock(card);
|
|
|
|
if (r < 0) {
|
|
|
|
check_ret(r, SC_AC_OP_READ, "Read failed", file);
|
2015-12-01 15:20:17 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
|
|
|
|
2021-02-03 21:50:23 +00:00
|
|
|
/* workaround when the issuer of a card does prefix the EF.ATR payload with 0x80 */
|
2021-03-22 12:50:50 +00:00
|
|
|
if (offsu == 0 /* do not apply the workaround if any offset */
|
|
|
|
&& r >= 1
|
|
|
|
&& buf[0] == ISO7816_II_CATEGORY_TLV
|
|
|
|
&& path.len >= 4
|
|
|
|
&& memcmp(path.value, "\x3f\x00\x2f\x01", 4) == 0)
|
2021-02-03 21:50:23 +00:00
|
|
|
offs++;
|
2020-05-21 08:50:53 +00:00
|
|
|
/* if offset does not exceed the length read from file/record, ... */
|
|
|
|
if (offs <= (unsigned int) r) {
|
|
|
|
/* ... perform the ASN.1 dump */
|
|
|
|
sc_asn1_print_tags(buf + offs, (unsigned int) r - offs);
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
}
|
2007-09-27 18:19:17 +00:00
|
|
|
|
2008-05-26 10:46:16 +00:00
|
|
|
err:
|
2007-09-27 18:19:17 +00:00
|
|
|
if (not_current) {
|
2017-01-05 19:21:44 +00:00
|
|
|
sc_file_free(file);
|
2010-08-20 22:51:42 +00:00
|
|
|
select_current_path_or_die();
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
2008-05-26 10:46:16 +00:00
|
|
|
return -err;
|
2007-09-27 18:19:17 +00:00
|
|
|
}
|
|
|
|
|
2013-01-06 12:13:08 +00:00
|
|
|
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) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Not supported\n");
|
2013-01-06 12:13:08 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
r = card->sm_ctx.ops.open(card);
|
|
|
|
}
|
|
|
|
else if (!strcmp(argv[0],"close")) {
|
|
|
|
if (!card->sm_ctx.ops.close) {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Not supported\n");
|
2013-01-06 12:13:08 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
r = card->sm_ctx.ops.close(card);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
ret = 0;
|
|
|
|
printf("Success!\n");
|
|
|
|
}
|
|
|
|
else {
|
2018-07-14 15:15:25 +00:00
|
|
|
fprintf(stderr, "Failure: %s\n", sc_strerror(r));
|
2013-01-06 12:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
static int do_help(int argc, char **argv)
|
2002-02-21 18:53:23 +00:00
|
|
|
{
|
2011-06-02 11:21:25 +00:00
|
|
|
struct command *cmd;
|
|
|
|
|
2018-07-14 19:46:41 +00:00
|
|
|
printf("%s commands:\n", (argc) ? "Matching" : "Supported");
|
2011-06-02 11:21:25 +00:00
|
|
|
|
|
|
|
for (cmd = cmds; cmd->name; cmd++) {
|
2018-07-14 19:46:41 +00:00
|
|
|
int i;
|
|
|
|
int match = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if (strncmp(cmd->name, argv[i], strlen(argv[i])) == 0)
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
if (match || !argc) {
|
|
|
|
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);
|
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
}
|
2018-07-14 19:46:41 +00:00
|
|
|
|
2002-02-21 18:53:23 +00:00
|
|
|
return 0;
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 11:21:25 +00:00
|
|
|
static int do_quit(int argc, char **argv)
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2011-06-02 11:21:25 +00:00
|
|
|
die(0);
|
|
|
|
return 0;
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 08:18:14 +00:00
|
|
|
static int parse_cmdline(char *in, char **argv, int argvsize)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
|
|
|
int argc;
|
|
|
|
|
2018-07-22 08:18:14 +00:00
|
|
|
for (argc = 0; argc < argvsize-1; argc++) {
|
2002-02-10 18:04:03 +00:00
|
|
|
in += strspn(in, " \t\n");
|
2018-07-22 08:18:14 +00:00
|
|
|
|
|
|
|
if (*in == '\0') { /* end of input reached */
|
|
|
|
argv[argc] = NULL;
|
2002-02-10 18:04:03 +00:00
|
|
|
return argc;
|
2018-07-22 08:18:14 +00:00
|
|
|
}
|
|
|
|
if (*in == '"') { /* double-quoted string */
|
2002-02-10 18:04:03 +00:00
|
|
|
argv[argc] = in++;
|
|
|
|
in += strcspn(in, "\"");
|
2018-07-22 08:18:14 +00:00
|
|
|
if (*in++ != '"') { /* error: unbalanced quote */
|
|
|
|
argv[0] = NULL;
|
2002-02-10 18:04:03 +00:00
|
|
|
return 0;
|
2018-07-22 08:18:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else { /* white-space delimited word */
|
2012-08-11 18:27:49 +00:00
|
|
|
argv[argc] = in;
|
2002-02-10 18:04:03 +00:00
|
|
|
in += strcspn(in, " \t\n");
|
|
|
|
}
|
|
|
|
if (*in != '\0')
|
|
|
|
*in++ = '\0';
|
2012-08-11 18:27:49 +00:00
|
|
|
}
|
2018-07-22 08:18:14 +00:00
|
|
|
|
|
|
|
/* error: too many arguments - argv[] exhausted */
|
|
|
|
argv[0] = NULL;
|
|
|
|
return 0;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2012-05-20 16:18:25 +00:00
|
|
|
static char *read_cmdline(FILE *script, char *prompt)
|
2002-02-10 18:04:03 +00:00
|
|
|
{
|
2018-07-22 09:03:23 +00:00
|
|
|
static char buf[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2002-02-10 18:04:03 +00:00
|
|
|
|
2018-09-16 10:51:50 +00:00
|
|
|
if (interactive) {
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef ENABLE_READLINE
|
2018-09-23 09:47:51 +00:00
|
|
|
static int initialized;
|
|
|
|
|
2018-09-16 10:51:50 +00:00
|
|
|
if (!initialized) {
|
|
|
|
initialized = 1;
|
2012-05-20 15:12:14 +00:00
|
|
|
using_history();
|
2018-09-16 10:51:50 +00:00
|
|
|
}
|
2018-07-22 09:03:23 +00:00
|
|
|
|
2005-01-21 18:47:41 +00:00
|
|
|
char *line = readline(prompt);
|
2018-07-22 09:03:23 +00:00
|
|
|
|
|
|
|
/* add line to history if longer than 2 characters */
|
|
|
|
if (line != NULL && strlen(line) > 2)
|
2005-01-21 18:47:41 +00:00
|
|
|
add_history(line);
|
2018-07-22 09:03:23 +00:00
|
|
|
|
|
|
|
/* return in interactive case with readline */
|
2005-01-21 18:47:41 +00:00
|
|
|
return line;
|
2018-07-22 09:03:23 +00:00
|
|
|
#else
|
2018-11-25 21:08:36 +00:00
|
|
|
sc_color_fprintf(SC_COLOR_FG_BLUE|SC_COLOR_BOLD, NULL, stdout, "%s", prompt);
|
2002-03-15 10:40:35 +00:00
|
|
|
#endif
|
2018-07-22 09:03:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* either we don't have readline or we are not running interactively */
|
2002-02-10 18:04:03 +00:00
|
|
|
fflush(stdout);
|
2018-11-25 21:08:36 +00:00
|
|
|
if (fgets(buf, sizeof(buf), script) == NULL) {
|
|
|
|
fputc('\n', stdout);
|
2002-02-10 18:04:03 +00:00
|
|
|
return NULL;
|
2018-11-25 21:08:36 +00:00
|
|
|
}
|
2002-02-10 18:04:03 +00:00
|
|
|
if (strlen(buf) == 0)
|
|
|
|
return NULL;
|
|
|
|
if (buf[strlen(buf)-1] == '\n')
|
|
|
|
buf[strlen(buf)-1] = '\0';
|
2005-01-21 18:47:41 +00:00
|
|
|
return buf;
|
2002-02-10 18:04:03 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 21:12:58 +00:00
|
|
|
int main(int argc, char *argv[])
|
2002-01-09 01:03:10 +00:00
|
|
|
{
|
2002-01-20 21:20:09 +00:00
|
|
|
int r, c, long_optind = 0, err = 0;
|
2006-02-07 20:14:43 +00:00
|
|
|
sc_context_param_t ctx_param;
|
2010-09-13 08:08:42 +00:00
|
|
|
int lcycle = SC_CARDCTRL_LIFECYCLE_ADMIN;
|
2019-01-25 12:54:27 +00:00
|
|
|
FILE *script;
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2002-06-14 12:52:56 +00:00
|
|
|
printf("OpenSC Explorer version %s\n", sc_get_version());
|
2002-01-09 01:03:10 +00:00
|
|
|
|
2002-01-20 21:20:09 +00:00
|
|
|
while (1) {
|
2010-08-20 22:51:39 +00:00
|
|
|
c = getopt_long(argc, argv, "r:c:vwm:", options, &long_optind);
|
2002-01-20 21:20:09 +00:00
|
|
|
if (c == -1)
|
|
|
|
break;
|
|
|
|
if (c == '?')
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]");
|
2002-01-20 21:20:09 +00:00
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
2010-01-24 15:29:47 +00:00
|
|
|
opt_reader = optarg;
|
2002-01-20 21:20:09 +00:00
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
opt_driver = optarg;
|
|
|
|
break;
|
2003-01-03 16:58:32 +00:00
|
|
|
case 'w':
|
|
|
|
opt_wait = 1;
|
|
|
|
break;
|
2004-06-13 20:13:12 +00:00
|
|
|
case 'v':
|
|
|
|
verbose++;
|
|
|
|
break;
|
2010-08-20 22:51:39 +00:00
|
|
|
case 'm':
|
2010-08-18 13:42:26 +00:00
|
|
|
opt_startfile = optarg;
|
|
|
|
break;
|
2002-01-20 21:20:09 +00:00
|
|
|
}
|
|
|
|
}
|
2002-03-15 10:40:35 +00:00
|
|
|
|
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);
|
2002-01-09 01:03:10 +00:00
|
|
|
if (r) {
|
2002-01-20 21:20:09 +00:00
|
|
|
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
|
2002-01-09 01:03:10 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2010-10-20 12:33:07 +00:00
|
|
|
|
2015-07-22 15:25:35 +00:00
|
|
|
ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
|
2013-08-02 20:01:51 +00:00
|
|
|
|
2010-10-20 12:33:07 +00:00
|
|
|
if (verbose > 1) {
|
|
|
|
ctx->debug = verbose;
|
|
|
|
ctx->debug_file = stderr;
|
|
|
|
}
|
2003-01-03 16:58:32 +00:00
|
|
|
|
2002-01-20 21:20:09 +00:00
|
|
|
if (opt_driver != NULL) {
|
2019-12-25 18:12:31 +00:00
|
|
|
/* special card driver value "?" means: list available drivers */
|
|
|
|
if (strncmp("?", opt_driver, sizeof("?")) == 0) {
|
|
|
|
err = util_list_card_drivers(ctx);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2002-01-20 21:20:09 +00:00
|
|
|
err = sc_set_card_driver(ctx, opt_driver);
|
|
|
|
if (err) {
|
|
|
|
fprintf(stderr, "Driver '%s' not found!\n", opt_driver);
|
|
|
|
err = 1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
2003-01-03 16:58:32 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0, 0);
|
2003-01-03 16:58:32 +00:00
|
|
|
if (err)
|
2002-01-20 21:20:09 +00:00
|
|
|
goto end;
|
2003-01-03 16:58:32 +00:00
|
|
|
|
2010-08-18 13:42:26 +00:00
|
|
|
if (opt_startfile) {
|
2010-08-20 22:51:39 +00:00
|
|
|
if(*opt_startfile) {
|
2018-07-14 11:37:18 +00:00
|
|
|
char startpath[SC_MAX_PATH_STRING_SIZE * 2];
|
2011-05-22 11:35:42 +00:00
|
|
|
char *args[] = { startpath };
|
2010-09-13 08:08:42 +00:00
|
|
|
|
2018-07-14 11:37:18 +00:00
|
|
|
if (strlcpy(startpath, opt_startfile, sizeof(startpath)) >= sizeof(startpath)) {
|
|
|
|
fprintf(stderr, "unable to select file %s: name too long\n",
|
|
|
|
opt_startfile);
|
|
|
|
die(1);
|
|
|
|
}
|
2011-05-22 11:35:42 +00:00
|
|
|
r = do_cd(1, args);
|
2010-08-20 22:51:39 +00:00
|
|
|
if (r) {
|
2018-07-08 08:52:30 +00:00
|
|
|
fprintf(stderr, "unable to select file %s: %s\n",
|
2010-08-20 22:51:39 +00:00
|
|
|
opt_startfile, sc_strerror(r));
|
2018-07-08 08:52:30 +00:00
|
|
|
die(1);
|
2010-08-20 22:51:39 +00:00
|
|
|
}
|
2010-08-18 13:42:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sc_format_path("3F00", ¤t_path);
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_select_file(card, ¤t_path, ¤t_file);
|
|
|
|
sc_unlock(card);
|
2010-08-18 13:42:26 +00:00
|
|
|
if (r) {
|
2018-07-08 08:52:30 +00:00
|
|
|
fprintf(stderr, "unable to select MF: %s\n", sc_strerror(r));
|
|
|
|
die(1);
|
2010-08-18 13:42:26 +00:00
|
|
|
}
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2018-05-18 21:21:14 +00:00
|
|
|
r = sc_lock(card);
|
|
|
|
if (r == SC_SUCCESS)
|
|
|
|
r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle);
|
|
|
|
sc_unlock(card);
|
2010-09-13 08:08:42 +00:00
|
|
|
if (r && r != SC_ERROR_NOT_SUPPORTED)
|
|
|
|
printf("unable to change lifecycle: %s\n", sc_strerror(r));
|
|
|
|
|
2012-05-20 15:12:14 +00:00
|
|
|
switch (argc - optind) {
|
|
|
|
default:
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]");
|
2012-05-20 15:12:14 +00:00
|
|
|
break;
|
|
|
|
case 0:
|
2018-09-16 10:51:50 +00:00
|
|
|
interactive = 1;
|
2012-05-20 15:12:14 +00:00
|
|
|
script = stdin;
|
|
|
|
break;
|
|
|
|
case 1:
|
2018-09-16 10:51:50 +00:00
|
|
|
interactive = 0;
|
2012-05-20 15:12:14 +00:00
|
|
|
if (strcmp(argv[optind], "-") == 0) {
|
|
|
|
script = stdin;
|
|
|
|
}
|
|
|
|
else if ((script = fopen(argv[optind], "r")) == NULL) {
|
2012-05-26 16:53:09 +00:00
|
|
|
util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]");
|
2012-05-20 15:12:14 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!feof(script)) {
|
2018-07-14 19:05:54 +00:00
|
|
|
char *line;
|
|
|
|
int cargc;
|
|
|
|
char *cargv[260];
|
|
|
|
int multiple;
|
2005-02-04 18:10:23 +00:00
|
|
|
struct command *cmd;
|
2011-06-02 17:47:50 +00:00
|
|
|
char prompt[3*SC_MAX_PATH_STRING_SIZE];
|
|
|
|
|
2020-02-02 11:57:08 +00:00
|
|
|
sprintf(prompt, "OpenSC [%s]> ", path_to_filename(¤t_path, '/', 0));
|
2012-05-20 16:18:25 +00:00
|
|
|
line = read_cmdline(script, prompt);
|
2005-01-21 18:47:41 +00:00
|
|
|
if (line == NULL)
|
|
|
|
break;
|
2018-07-22 08:18:14 +00:00
|
|
|
|
2012-05-20 16:18:25 +00:00
|
|
|
cargc = parse_cmdline(line, cargv, DIM(cargv));
|
2012-05-20 15:12:14 +00:00
|
|
|
if ((cargc < 1) || (*cargv[0] == '#'))
|
2002-01-09 01:03:10 +00:00
|
|
|
continue;
|
2018-07-22 08:18:14 +00:00
|
|
|
|
2018-07-14 19:05:54 +00:00
|
|
|
cmd = ambiguous_match(cmds, cargv[0], &multiple);
|
2005-02-04 18:10:23 +00:00
|
|
|
if (cmd == NULL) {
|
2018-07-14 19:05:54 +00:00
|
|
|
fprintf(stderr, "%s command: %s\n",
|
|
|
|
(multiple) ? "Ambiguous" : "Unknown", cargv[0]);
|
2018-07-08 08:52:30 +00:00
|
|
|
if (interactive)
|
|
|
|
do_help((multiple) ? 1 : 0, cargv);
|
|
|
|
err = -1;
|
2002-02-21 18:53:23 +00:00
|
|
|
} else {
|
2018-07-08 08:52:30 +00:00
|
|
|
err = cmd->func(cargc-1, cargv+1);
|
2002-01-09 01:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
2002-01-20 21:20:09 +00:00
|
|
|
end:
|
|
|
|
die(err);
|
2011-06-02 11:21:25 +00:00
|
|
|
|
2002-01-09 01:03:10 +00:00
|
|
|
return 0; /* not reached */
|
|
|
|
}
|