diff options
author | Werner Koch <[email protected]> | 2004-01-27 16:40:42 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2004-01-27 16:40:42 +0000 |
commit | eb24d8b751750cf96cb200f80b45ed3806648883 (patch) | |
tree | e78feb11795ff62728bc443352006ae33e50ec9c /scd/sc-investigate.c | |
parent | * call-scd.c (atfork_cb): New. (diff) | |
download | gnupg-eb24d8b751750cf96cb200f80b45ed3806648883.tar.gz gnupg-eb24d8b751750cf96cb200f80b45ed3806648883.zip |
Some minor bug fixes, new test utilities and started support for other
smartcard applications.
Diffstat (limited to '')
-rw-r--r-- | scd/sc-investigate.c | 480 |
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: + ; +} + |