1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

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 * 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 * RESET command is send to scd. With HEXAPDU being the string
* "undefined" the command "SERIALNO undefined" is send to scd. If * "undefined" the command "SERIALNO undefined" is send to scd. If
* R_DATA is not NULL the data is without the status code is stored * R_DATA is not NULL the data without the status code is stored
* there. Caller must release it. */ * there. Caller must release it. If OPTIONS is not NULL, this will
* be passed verbatim to the SCDaemon's APDU command. */
gpg_error_t 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) unsigned char **r_data, size_t *r_datalen)
{ {
gpg_error_t err; gpg_error_t err;
@ -487,7 +488,8 @@ scd_apdu (const char *hexapdu, unsigned int *r_sw,
init_membuf (&mb, 256); 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, err = assuan_transact (agent_ctx, line,
put_membuf_cb, &mb, NULL, NULL, NULL, NULL); put_membuf_cb, &mb, NULL, NULL, NULL, NULL);
if (!err) if (!err)

View File

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

View File

@ -3137,6 +3137,99 @@ cmd_yubikey (card_info_t info, char *argstr)
return err; 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 /* 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, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP, cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
cmdUIF, cmdAUTH, cmdYUBIKEY, cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU,
cmdINVCMD cmdINVCMD
}; };
@ -3189,6 +3282,7 @@ static struct
{ "writecert", cmdWRITECERT, N_("store a certificate to a data object")}, { "writecert", cmdWRITECERT, N_("store a certificate to a data object")},
{ "writekey", cmdWRITEKEY, N_("store a private key to a data object")}, { "writekey", cmdWRITEKEY, N_("store a private key to a data object")},
{ "yubikey", cmdYUBIKEY, N_("Yubikey management commands")}, { "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
{ "apdu", cmdAPDU, NULL},
{ NULL, cmdINVCMD, NULL } { NULL, cmdINVCMD, NULL }
}; };
@ -3284,7 +3378,7 @@ dispatch_command (card_info_t info, const char *orig_command)
else else
{ {
flush_keyblock_cache (); flush_keyblock_cache ();
err = scd_apdu (NULL, NULL, NULL, NULL); err = scd_apdu (NULL, NULL, NULL, NULL, NULL);
if (!err) if (!err)
info->need_sn_cmd = 1; 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 cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break; case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break; case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdAPDU: err = cmd_apdu (info, argstr); break;
case cmdINVCMD: case cmdINVCMD:
default: default:
@ -3503,7 +3598,7 @@ interactive_loop (void)
else else
{ {
flush_keyblock_cache (); flush_keyblock_cache ();
err = scd_apdu (NULL, NULL, NULL, NULL); err = scd_apdu (NULL, NULL, NULL, NULL, NULL);
if (!err) if (!err)
info->need_sn_cmd = 1; info->need_sn_cmd = 1;
} }
@ -3539,6 +3634,7 @@ interactive_loop (void)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break; case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break; case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break; case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdAPDU: err = cmd_apdu (info, argstr); break;
case cmdINVCMD: case cmdINVCMD:
default: 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); void release_card_info (card_info_t info);
const char *app_type_string (app_type_t app_type); 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); unsigned char **r_data, size_t *r_datalen);
gpg_error_t scd_switchcard (const char *serialno); gpg_error_t scd_switchcard (const char *serialno);