aboutsummaryrefslogtreecommitdiffstats
path: root/scd/sc-investigate.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scd/sc-investigate.c480
1 files changed, 451 insertions, 29 deletions
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
index ecd385690..acef86ead 100644
--- a/scd/sc-investigate.c
+++ b/scd/sc-investigate.c
@@ -24,6 +24,13 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#ifdef HAVE_READLINE_READLINE_H
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
@@ -32,17 +39,25 @@
#include "apdu.h" /* for open_reader */
#include "atr.h"
#include "app-common.h"
+#include "iso7816.h"
#define _(a) (a)
+#define CONTROL_D ('D' - 'A' + 1)
+
enum cmd_and_opt_values
-{ oVerbose = 'v',
+{
+ oInteractive = 'i',
+ oVerbose = 'v',
oReaderPort = 500,
octapiDriver,
oDebug,
oDebugAll,
+ oDisableCCID,
+
+
oGenRandom,
aTest };
@@ -52,15 +67,27 @@ static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, "@Options:\n " },
+ { oInteractive, "interactive", 0, "start in interactive explorer mode"},
{ oVerbose, "verbose", 0, "verbose" },
{ oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
{ octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"},
+ { oDisableCCID, "disable-ccid", 0,
+#ifdef HAVE_LIBUSB
+ "do not use the internal CCID driver"
+#else
+ "@"
+#endif
+ },
{ oDebug, "debug" ,4|16, "set debugging flags"},
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
{ oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
{0}
};
+
+static void interactive_shell (int slot);
+
+
static const char *
my_strusage (int level)
{
@@ -111,10 +138,8 @@ main (int argc, char **argv )
ARGPARSE_ARGS pargs;
int slot, rc;
const char *reader_port = NULL;
- struct app_ctx_s appbuf;
unsigned long gen_random = 0;
-
- memset (&appbuf, 0, sizeof appbuf);
+ int interactive = 0;
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
@@ -143,7 +168,9 @@ main (int argc, char **argv )
case oDebugAll: opt.debug = ~0; break;
case oReaderPort: reader_port = pargs.r.ret_str; break;
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
+ case oDisableCCID: opt.disable_ccid = 1; break;
case oGenRandom: gen_random = pargs.r.ret_ulong; break;
+ case oInteractive: interactive = 1; break;
default : pargs.err = 2; break;
}
}
@@ -151,7 +178,7 @@ main (int argc, char **argv )
exit(2);
if (opt.verbose < 2)
- opt.verbose = 2; /* hack to let select_openpgp print some info. */
+ opt.verbose = 2; /* Hack to let select_openpgp print some info. */
if (argc)
usage (1);
@@ -167,40 +194,61 @@ main (int argc, char **argv )
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
}
- appbuf.slot = slot;
- rc = app_select_openpgp (&appbuf, NULL, NULL);
- if (rc)
- log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ if (interactive)
+ interactive_shell (slot);
else
{
- appbuf.initialized = 1;
- log_info ("openpgp application selected\n");
+ struct app_ctx_s appbuf;
- if (gen_random)
+ /* Fixme: We better use app.c directly. */
+ memset (&appbuf, 0, sizeof appbuf);
+ appbuf.slot = slot;
+ rc = app_select_openpgp (&appbuf);
+ if (rc)
{
- size_t nbytes;
- unsigned char *buffer;
-
- buffer = xmalloc (4096);
- do
+ log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ memset (&appbuf, 0, sizeof appbuf);
+ appbuf.slot = slot;
+ rc = app_select_dinsig (&appbuf);
+ if (rc)
+ log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
+ else
{
- nbytes = gen_random > 4096? 4096 : gen_random;
- rc = app_get_challenge (&appbuf, nbytes, buffer);
- if (rc)
- log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
- else
+ appbuf.initialized = 1;
+ log_info ("dinsig application selected\n");
+ }
+ }
+ else
+ {
+ appbuf.initialized = 1;
+ log_info ("openpgp application selected\n");
+
+ if (gen_random)
+ {
+ size_t nbytes;
+ unsigned char *buffer;
+
+ buffer = xmalloc (4096);
+ do
{
- if (fwrite (buffer, nbytes, 1, stdout) != 1)
- log_error ("writing to stdout failed: %s\n",
- strerror (errno));
- gen_random -= nbytes;
+ nbytes = gen_random > 4096? 4096 : gen_random;
+ rc = app_get_challenge (&appbuf, nbytes, buffer);
+ if (rc)
+ log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
+ else
+ {
+ if (fwrite (buffer, nbytes, 1, stdout) != 1)
+ log_error ("writing to stdout failed: %s\n",
+ strerror (errno));
+ gen_random -= nbytes;
+ }
}
+ while (gen_random && !log_get_errorcount (0));
+ xfree (buffer);
}
- while (gen_random && !log_get_errorcount (0));
- xfree (buffer);
}
}
-
+
return log_get_errorcount (0)? 2:0;
}
@@ -211,3 +259,377 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
{
/* DUMMY */
}
+
+
+
+/* Dump BUFFER of length NBYTES in a nicely human readable format. */
+static void
+dump_buffer (const unsigned char *buffer, size_t nbytes)
+{
+ int i;
+
+ while (nbytes)
+ {
+ for (i=0; i < 16 && i < nbytes; i++)
+ printf ("%02X%s ", buffer[i], i==8? " ":"");
+ for (; i < 16; i++)
+ printf (" %s ", i==8? " ":"");
+ putchar (' ');
+ putchar (' ');
+ for (i=0; i < 16 && i < nbytes; i++)
+ if (isprint (buffer[i]))
+ putchar (buffer[i]);
+ else
+ putchar ('.');
+ nbytes -= i;
+ buffer += i;
+ for (; i < 16; i++)
+ putchar (' ');
+ putchar ('\n');
+ }
+}
+
+
+static void
+dump_or_store_buffer (const char *arg,
+ const unsigned char *buffer, size_t nbytes)
+{
+ const char *s = strchr (arg, '>');
+ int append;
+ FILE *fp;
+
+ if (!s)
+ {
+ dump_buffer (buffer, nbytes);
+ return;
+ }
+ if ((append = (*++s == '>')))
+ s++;
+ fp = fopen (s, append? "ab":"wb");
+ if (!fp)
+ {
+ log_error ("failed to create `%s': %s\n", s, strerror (errno));
+ return;
+ }
+ if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1)
+ log_error ("failed to write to `%s': %s\n", s, strerror (errno));
+ if (fclose (fp))
+ log_error ("failed to close `%s': %s\n", s, strerror (errno));
+}
+
+
+/* Convert STRING into a a newly allocated buffer and return the
+ length of the buffer in R_LENGTH. Detect xx:xx:xx... sequence and
+ unhexify that one. */
+static unsigned char *
+pin_to_buffer (const char *string, size_t *r_length)
+{
+ unsigned char *buffer = xmalloc (strlen (string)+1);
+ const char *s;
+ size_t n;
+
+ for (s=string, n=0; *s; s += 3)
+ {
+ if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2]))
+ {
+ buffer[n++] = xtoi_2 (s);
+ if (!s[2])
+ break;
+ }
+ else
+ {
+ memcpy (buffer, string, strlen (string));
+ *r_length = strlen (string);
+ return buffer;
+ }
+ }
+ *r_length = n;
+ return buffer;
+}
+
+
+static char *
+read_line (int use_readline, char *prompt)
+{
+ static char buf[256];
+
+#ifdef HAVE_READLINE
+ if (use_readline)
+ {
+ char *line = readline (prompt);
+ if (line)
+ trim_spaces (line);
+ if (line && strlen (line) > 2 )
+ add_history (line);
+ return line;
+ }
+#endif
+ /* Either we don't have readline or we are not running
+ interactively */
+#ifndef HAVE_READLINE
+ printf ("%s", prompt );
+#endif
+ fflush(stdout);
+ if (!fgets(buf, sizeof(buf), stdin))
+ return NULL;
+ if (!strlen(buf))
+ return NULL;
+ if (buf[strlen (buf)-1] == '\n')
+ buf[strlen (buf)-1] = 0;
+ trim_spaces (buf);
+ return buf;
+}
+
+/* Run a shell for interactive exploration of the card. */
+static void
+interactive_shell (int slot)
+{
+ enum cmdids
+ {
+ cmdNOP = 0,
+ cmdQUIT, cmdHELP,
+ cmdSELECT,
+ cmdCHDIR,
+ cmdLS,
+ cmdAPP,
+ cmdREAD,
+ cmdREADREC,
+ cmdDEBUG,
+ cmdVERIFY,
+ cmdCHANGEREF,
+
+ cmdINVCMD
+ };
+ static struct
+ {
+ const char *name;
+ enum cmdids id;
+ const char *desc;
+ } cmds[] = {
+ { "quit" , cmdQUIT , "quit this menu" },
+ { "q" , cmdQUIT , NULL },
+ { "help" , cmdHELP , "show this help" },
+ { "?" , cmdHELP , NULL },
+ { "debug" , cmdDEBUG, "set debugging flags" },
+ { "select" , cmdSELECT, "select file (EF)" },
+ { "s" , cmdSELECT, NULL },
+ { "chdir" , cmdCHDIR, "change directory (select DF)"},
+ { "cd" , cmdCHDIR, NULL },
+ { "ls" , cmdLS, "list directory (some cards only)"},
+ { "app" , cmdAPP, "select application"},
+ { "read" , cmdREAD, "read binary" },
+ { "rb" , cmdREAD, NULL },
+ { "readrec", cmdREADREC, "read record(s)" },
+ { "rr" , cmdREADREC, NULL },
+ { "verify" , cmdVERIFY, "verify CHVNO PIN" },
+ { "ver" , cmdVERIFY, NULL },
+ { "changeref", cmdCHANGEREF, "change reference data" },
+ { NULL, cmdINVCMD }
+ };
+ enum cmdids cmd = cmdNOP;
+ int use_readline = isatty (fileno(stdin));
+ char *line;
+ gpg_error_t err = 0;
+ unsigned char *result = NULL;
+ size_t resultlen;
+
+#ifdef HAVE_READLINE
+ if (use_readline)
+ using_history ();
+#endif
+
+ for (;;)
+ {
+ int arg_number;
+ const char *arg_string = "";
+ const char *arg_next = "";
+ char *p;
+ int i;
+
+ if (err)
+ printf ("command failed: %s\n", gpg_strerror (err));
+ err = 0;
+ xfree (result);
+ result = NULL;
+
+ printf ("\n");
+ do
+ {
+ line = read_line (use_readline, "cmd> ");
+ }
+ while ( line && *line == '#' );
+
+ arg_number = 0;
+ if (!line || *line == CONTROL_D)
+ cmd = cmdQUIT;
+ else if (!*line)
+ cmd = cmdNOP;
+ else {
+ if ((p=strchr (line,' ')))
+ {
+ char *endp;
+
+ *p++ = 0;
+ trim_spaces (line);
+ trim_spaces (p);
+ arg_number = strtol (p, &endp, 0);
+ arg_string = p;
+ if (endp != p)
+ {
+ arg_next = endp;
+ while ( spacep (arg_next) )
+ arg_next++;
+ }
+ }
+
+ for (i=0; cmds[i].name; i++ )
+ if (!ascii_strcasecmp (line, cmds[i].name ))
+ break;
+
+ cmd = cmds[i].id;
+ }
+
+ switch (cmd)
+ {
+ case cmdHELP:
+ for (i=0; cmds[i].name; i++ )
+ if (cmds[i].desc)
+ printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
+ break;
+
+ case cmdQUIT:
+ goto leave;
+
+ case cmdNOP:
+ break;
+
+ case cmdDEBUG:
+ if (!*arg_string)
+ opt.debug = opt.debug? 0 : 2048;
+ else
+ opt.debug = arg_number;
+ break;
+
+ case cmdSELECT:
+ err = iso7816_select_file (slot, arg_number, 0, NULL, NULL);
+ break;
+
+ case cmdCHDIR:
+ err = iso7816_select_file (slot, arg_number, 1, NULL, NULL);
+ break;
+
+ case cmdLS:
+ err = iso7816_list_directory (slot, 1, &result, &resultlen);
+ if (!err || gpg_err_code (err) == GPG_ERR_ENOENT)
+ err = iso7816_list_directory (slot, 0, &result, &resultlen);
+ /* FIXME: Do something with RESULT. */
+ break;
+
+ case cmdAPP:
+ {
+ app_t app;
+
+ app = select_application (NULL, slot, *arg_string? arg_string:NULL);
+ if (app)
+ {
+ char *sn;
+
+ app_get_serial_and_stamp (app, &sn, NULL);
+ log_info ("application `%s' ready; sn=%s\n",
+ app->apptype?app->apptype:"?", sn? sn:"[none]");
+ release_application (app);
+ }
+ }
+ break;
+
+ case cmdREAD:
+ err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+ if (!err)
+ dump_or_store_buffer (arg_string, result, resultlen);
+ break;
+
+ case cmdREADREC:
+ if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' '))
+ {
+ /* Fixme: Can't write to a file yet. */
+ for (i=1, err=0; !err; i++)
+ {
+ xfree (result); result = NULL;
+ err = iso7816_read_record (slot, i, 1, &result, &resultlen);
+ if (!err)
+ dump_buffer (result, resultlen);
+ }
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ }
+ else
+ {
+ err = iso7816_read_record (slot, arg_number, 1,
+ &result, &resultlen);
+ if (!err)
+ dump_or_store_buffer (arg_string, result, resultlen);
+ }
+ break;
+
+ case cmdVERIFY:
+ if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
+ printf ("error: invalid CHVNO\n");
+ else
+ {
+ unsigned char *pin;
+ size_t pinlen;
+
+ pin = pin_to_buffer (arg_next, &pinlen);
+ err = iso7816_verify (slot, arg_number, pin, pinlen);
+ xfree (pin);
+ }
+ break;
+
+ case cmdCHANGEREF:
+ {
+ const char *newpin = arg_next;
+
+ while ( *newpin && !spacep (newpin) )
+ newpin++;
+ while ( spacep (newpin) )
+ newpin++;
+
+ if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
+ printf ("error: invalid CHVNO\n");
+ else if (!*arg_next || !*newpin || newpin == arg_next)
+ printf ("usage: changeref CHVNO OLDPIN NEWPIN\n");
+ else
+ {
+ char *oldpin = xstrdup (arg_next);
+ unsigned char *oldpin_buf, *newpin_buf;
+ size_t oldpin_len, newpin_len;
+
+ for (p=oldpin; *p && !spacep (p); p++ )
+ ;
+ *p = 0;
+ oldpin_buf = pin_to_buffer (oldpin, &oldpin_len);
+ newpin_buf = pin_to_buffer (newpin, &newpin_len);
+
+ err = iso7816_change_reference_data (slot, arg_number,
+ oldpin_buf, oldpin_len,
+ newpin_buf, newpin_len);
+
+ xfree (newpin_buf);
+ xfree (oldpin_buf);
+ xfree (oldpin);
+ }
+ }
+ break;
+
+ case cmdINVCMD:
+ default:
+ printf ("\n");
+ printf ("Invalid command (try \"help\")\n");
+ break;
+ } /* End command switch. */
+ } /* End of main menu loop. */
+
+ leave:
+ ;
+}
+