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:
parent
94d31660c6
commit
ed0759f39b
@ -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)
|
||||||
|
@ -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));
|
||||||
|
102
tools/gpg-card.c
102
tools/gpg-card.c
@ -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:
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user