card: New command "apdu"

* tools/card-call-scd.c (scd_apdu): Add optional arg 'options'.
* tools/gpg-card.c (cmd_apdu): New.
(enum cmdids): Add cmdAPDU.
(dispatch_command): Add command "apdu".
(interactive_loop): Ditto.
--

This command is hidden because it can be used to brick a card.  The
command is basically the same as sending "scd apdu" in
gpg-connect-agent but here we do full decoding and printing in hex.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-05-28 13:00:27 +02:00
parent 94d31660c6
commit ed0759f39b
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 108 additions and 9 deletions

View File

@ -450,10 +450,11 @@ store_serialno (const char *line)
* stored at R_SW inless R_SW is NULL. With HEXAPDU being NULL only a
* RESET command is send to scd. With HEXAPDU being the string
* "undefined" the command "SERIALNO undefined" is send to scd. If
* R_DATA is not NULL the data is without the status code is stored
* there. Caller must release it. */
* R_DATA is not NULL the data without the status code is stored
* there. Caller must release it. If OPTIONS is not NULL, this will
* be passed verbatim to the SCDaemon's APDU command. */
gpg_error_t
scd_apdu (const char *hexapdu, unsigned int *r_sw,
scd_apdu (const char *hexapdu, const char *options, unsigned int *r_sw,
unsigned char **r_data, size_t *r_datalen)
{
gpg_error_t err;
@ -487,7 +488,8 @@ scd_apdu (const char *hexapdu, unsigned int *r_sw,
init_membuf (&mb, 256);
snprintf (line, DIM(line), "SCD APDU %s", hexapdu);
snprintf (line, DIM(line), "SCD APDU %s%s%s",
options?options:"", options?" -- ":"", hexapdu);
err = assuan_transact (agent_ctx, line,
put_membuf_cb, &mb, NULL, NULL, NULL, NULL);
if (!err)

View File

@ -88,7 +88,7 @@ send_apdu (const char *hexapdu, const char *desc, unsigned int ignore,
gpg_error_t err;
unsigned int sw;
err = scd_apdu (hexapdu, &sw, r_data, r_datalen);
err = scd_apdu (hexapdu, NULL, &sw, r_data, r_datalen);
if (err)
log_error ("sending card command %s failed: %s\n", desc,
gpg_strerror (err));

View File

@ -3137,6 +3137,99 @@ cmd_yubikey (card_info_t info, char *argstr)
return err;
}
static gpg_error_t
cmd_apdu (card_info_t info, char *argstr)
{
gpg_error_t err;
estream_t fp = opt.interactive? NULL : es_stdout;
int with_atr;
int handle_more;
const char *s;
const char *exlenstr;
int exlenstrlen;
char *options = NULL;
unsigned int sw;
unsigned char *result = NULL;
size_t i, j, resultlen;
if (!info)
return print_help
("APDU [--more] [--exlen[=N]] <hexstring>\n"
"\n"
"Send an APDU to the current card. This command bypasses the high\n"
"level functions and sends the data directly to the card. HEXSTRING\n"
"is expected to be a proper APDU.\n"
"\n"
"Using the option \"--more\" handles the card status word MORE_DATA\n"
"(61xx) and concatenates all responses to one block.\n"
"\n"
"Using the option \"--exlen\" the returned APDU may use extended\n"
"length up to N bytes. If N is not given a default value is used.\n",
0);
if (has_option (argstr, "--dump-atr"))
with_atr = 2;
else
with_atr = has_option (argstr, "--atr");
handle_more = has_option (argstr, "--more");
exlenstr = has_option_name (argstr, "--exlen");
exlenstrlen = 0;
if (exlenstr)
{
for (s=exlenstr; *s && !spacep (s); s++)
exlenstrlen++;
}
argstr = skip_options (argstr);
if (with_atr || handle_more || exlenstr)
options = xasprintf ("%s%s%s%.*s",
with_atr == 2? " --dump-atr": with_atr? " --atr":"",
handle_more?" --more":"",
exlenstr?" ":"", exlenstrlen, exlenstr?exlenstr:"");
err = scd_apdu (argstr, options, &sw, &result, &resultlen);
if (err)
goto leave;
log_info ("Statusword: 0x%04x\n", sw);
for (i=0; i < resultlen; )
{
size_t save_i = i;
tty_fprintf (fp, "D[%04X] ", (unsigned int)i);
for (j=0; j < 16 ; j++, i++)
{
if (j == 8)
tty_fprintf (fp, " ");
if (i < resultlen)
tty_fprintf (fp, " %02X", result[i]);
else
tty_fprintf (fp, " ");
}
tty_fprintf (fp, " ");
i = save_i;
for (j=0; j < 16; j++, i++)
{
unsigned int c = result[i];
if ( i >= resultlen )
tty_fprintf (fp, " ");
else if (isascii (c) && isprint (c) && !iscntrl (c))
tty_fprintf (fp, "%c", c);
else
tty_fprintf (fp, ".");
}
tty_fprintf (fp, "\n");
}
leave:
xfree (result);
xfree (options);
return err;
}
/* Data used by the command parser. This needs to be outside of the
@ -3148,7 +3241,7 @@ enum cmdids
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
cmdUIF, cmdAUTH, cmdYUBIKEY,
cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU,
cmdINVCMD
};
@ -3189,6 +3282,7 @@ static struct
{ "writecert", cmdWRITECERT, N_("store a certificate to a data object")},
{ "writekey", cmdWRITEKEY, N_("store a private key to a data object")},
{ "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
{ "apdu", cmdAPDU, NULL},
{ NULL, cmdINVCMD, NULL }
};
@ -3284,7 +3378,7 @@ dispatch_command (card_info_t info, const char *orig_command)
else
{
flush_keyblock_cache ();
err = scd_apdu (NULL, NULL, NULL, NULL);
err = scd_apdu (NULL, NULL, NULL, NULL, NULL);
if (!err)
info->need_sn_cmd = 1;
}
@ -3312,6 +3406,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdAPDU: err = cmd_apdu (info, argstr); break;
case cmdINVCMD:
default:
@ -3503,7 +3598,7 @@ interactive_loop (void)
else
{
flush_keyblock_cache ();
err = scd_apdu (NULL, NULL, NULL, NULL);
err = scd_apdu (NULL, NULL, NULL, NULL, NULL);
if (!err)
info->need_sn_cmd = 1;
}
@ -3539,6 +3634,7 @@ interactive_loop (void)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdAPDU: err = cmd_apdu (info, argstr); break;
case cmdINVCMD:
default:

View File

@ -208,7 +208,8 @@ gpg_error_t send_apdu (const char *hexapdu, const char *desc,
void release_card_info (card_info_t info);
const char *app_type_string (app_type_t app_type);
gpg_error_t scd_apdu (const char *hexapdu, unsigned int *r_sw,
gpg_error_t scd_apdu (const char *hexapdu, const char *options,
unsigned int *r_sw,
unsigned char **r_data, size_t *r_datalen);
gpg_error_t scd_switchcard (const char *serialno);