Add readcert command.

fix reading large certificates.
This commit is contained in:
Werner Koch 2009-06-17 09:45:50 +00:00
parent bdbeb0ac2b
commit 5f8acaccc0
10 changed files with 232 additions and 67 deletions

View File

@ -575,7 +575,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
/* Fixme: For unknown reasons AllowSetForegroundWindow returns an
invalid argument error if we pass the the correct processID to
invalid argument error if we pass the correct processID to
it. As a workaround we use -1 (ASFW_ANY). */
if ( (flags & 64) )
gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);

View File

@ -1,3 +1,15 @@
2009-06-17 Werner Koch <wk@g10code.com>
* card-util.c (put_data_to_file, read_cert): New.
(card_edit): Add command "readcert".
(fetch_url): Allow code also for this gnupg major version 2.
* call-agent.c (agent_scd_readcert): New.
2009-06-15 Werner Koch <wk@g10code.com>
* keyserver.c (keyserver_search_prompt): No prompt in batch+colons
mode.
2009-06-09 Werner Koch <wk@g10code.com>
* card-util.c (write_sc_op_status): New.

View File

@ -488,7 +488,6 @@ agent_scd_writecert (const char *certidstr,
}
/* Handle a KEYDATA inquiry. Note, we only send the data,
assuan_transact takes care of flushing and writing the end */
@ -539,7 +538,6 @@ agent_scd_writekey (int keyno, const char *serialno,
}
/* Status callback for the SCD GENKEY command. */
static int
@ -765,6 +763,43 @@ agent_scd_pkdecrypt (const char *serialno,
}
/* Send a READCERT command to the SCdaemon. */
int
agent_scd_readcert (const char *certidstr,
void **r_buf, size_t *r_buflen)
{
int rc;
char line[ASSUAN_LINELENGTH];
membuf_t data;
size_t len;
*r_buf = NULL;
rc = start_agent ();
if (rc)
return rc;
init_membuf (&data, 2048);
snprintf (line, DIM(line)-1, "SCD READCERT %s", certidstr);
line[DIM(line)-1] = 0;
rc = assuan_transact (agent_ctx, line,
membuf_data_cb, &data,
default_inq_cb, NULL, NULL, NULL);
if (rc)
{
xfree (get_membuf (&data, &len));
return rc;
}
*r_buf = get_membuf (&data, r_buflen);
if (!*r_buf)
return gpg_error (GPG_ERR_ENOMEM);
return 0;
}
/* Change the PIN of an OpenPGP card or reset the retry counter.
CHVNO 1: Change the PIN
2: For v1 cards: Same as 1.

View File

@ -104,6 +104,10 @@ int agent_scd_pkdecrypt (const char *serialno,
const unsigned char *indata, size_t indatalen,
unsigned char **r_buf, size_t *r_buflen);
/* Send a READKEY command to the SCdaemon. */
int agent_scd_readcert (const char *certidstr,
void **r_buf, size_t *r_buflen);
/* Change the PIN of an OpenPGP card or reset the retry counter. */
int agent_scd_change_pin (int chvno, const char *serialno);

View File

@ -715,7 +715,6 @@ change_url (void)
static int
fetch_url(void)
{
#if GNUPG_MAJOR_VERSION == 1
int rc;
struct agent_card_info_s info;
@ -755,15 +754,11 @@ fetch_url(void)
}
return rc;
#else
#warning need to implemented fucntion
return 0;
#endif
}
/* Read data from file FNAME up to MAXLEN characters. On error return
-1 and store NULl at R_BUFFER; on success return the number of
-1 and store NULL at R_BUFFER; on success return the number of
bytes read and store the address of a newly allocated buffer at
R_BUFFER. */
static int
@ -814,6 +809,39 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
}
/* Write LENGTH bytes from BUFFER to file FNAME. Return 0 on
success. */
static int
put_data_to_file (const char *fname, const void *buffer, size_t length)
{
FILE *fp;
fp = fopen (fname, "wb");
#if GNUPG_MAJOR_VERSION == 1
if (fp && is_secured_file (fileno (fp)))
{
fclose (fp);
fp = NULL;
errno = EPERM;
}
#endif
if (!fp)
{
tty_printf (_("can't create `%s': %s\n"), fname, strerror (errno));
return -1;
}
if (length && fwrite (buffer, length, 1, fp) != 1)
{
tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno));
fclose (fp);
return -1;
}
fclose (fp);
return 0;
}
static int
change_login (const char *args)
{
@ -933,6 +961,37 @@ change_cert (const char *args)
}
static int
read_cert (const char *args)
{
const char *fname;
void *buffer;
size_t length;
int rc;
if (args && *args == '>') /* Write it to a file */
{
for (args++; spacep (args); args++)
;
fname = args;
}
else
{
tty_printf ("usage error: redirectrion to file required\n");
return -1;
}
rc = agent_scd_readcert ("OPENPGP.3", &buffer, &length);
if (rc)
log_error ("error reading certificate from card: %s\n", gpg_strerror (rc));
else
rc = put_data_to_file (fname, buffer, length);
xfree (buffer);
write_sc_op_status (rc);
return rc;
}
static int
change_lang (void)
{
@ -1447,7 +1506,7 @@ enum cmdids
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdUNBLOCK,
cmdREADCERT, cmdUNBLOCK,
cmdINVCMD
};
@ -1481,6 +1540,7 @@ static struct
{ "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
/* Note, that we do not announce these command yet. */
{ "privatedo", cmdPRIVATEDO, 0, NULL },
{ "readcert", cmdREADCERT, 0, NULL },
{ "writecert", cmdWRITECERT, 1, NULL },
{ NULL, cmdINVCMD, 0, NULL }
};
@ -1735,6 +1795,13 @@ card_edit (strlist_t commands)
change_cert (arg_rest);
break;
case cmdREADCERT:
if ( arg_number != 3 )
tty_printf ("usage: readcert 3 > FILE\n");
else
read_cert (arg_rest);
break;
case cmdFORCESIG:
toggle_forcesig ();
break;

View File

@ -862,6 +862,9 @@ keyserver_search_prompt(IOBUF buffer,const char *searchstr)
if(i!=count)
validcount=0;
if (opt.with_colons && opt.batch)
break;
for(;;)
{
if(show_prompt(desc,i,validcount?count:0,localstr))

View File

@ -1,3 +1,17 @@
2009-06-17 Werner Koch <wk@g10code.com>
* iso7816.c (iso7816_get_data): Add arg EXTENDED_MODE. Change all
callers.
* app-openpgp.c (data_objects): Use bit flags. Add flag
TRY_EXTLENGTH.
(get_cached_data): Add arg TRY_EXTLEN and use it for iso7816_get_data.
(get_one_do): Use extended length APDU if necessary.
2009-06-10 Werner Koch <wk@g10code.com>
* app-openpgp.c (store_fpr): Change first arg to app_t; adjust
callers. Flush the cache.
2009-06-09 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_readcert): Return NOT_FOUND if the retrieved

View File

@ -75,43 +75,45 @@ static struct {
int tag;
int constructed;
int get_from; /* Constructed DO with this DO or 0 for direct access. */
int binary;
int dont_cache;
int flush_on_error;
int get_immediate_in_v11; /* Enable a hack to bypass the cache of
this data object if it is used in 1.1
and later versions of the card. This
does not work with composite DO and is
currently only useful for the CHV
status bytes. */
int binary:1;
int dont_cache:1;
int flush_on_error:1;
int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
this data object if it is used in 1.1
and later versions of the card. This
does not work with composite DO and
is currently only useful for the CHV
status bytes. */
int try_extlen:1; /* Large object; try to use an extended
length APDU. */
char *desc;
} data_objects[] = {
{ 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" },
{ 0x5F50, 0, 0, 0, 0, 0, 0, "URL" },
{ 0x5F52, 0, 0, 1, 0, 0, 0, "Historical Bytes" },
{ 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
{ 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" },
{ 0x006E, 1, 0, 1, 0, 0, 0, "Application Related Data" },
{ 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" },
{ 0x0073, 1, 0, 1, 0, 0, 0, "Discretionary Data Objects" },
{ 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" },
{ 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" },
{ 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" },
{ 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" },
{ 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" },
{ 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" },
{ 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" },
{ 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" },
{ 0x00CD, 0, 0x6E, 1, 0, 0, 0, "Generation time" },
{ 0x007A, 1, 0, 1, 0, 0, 0, "Security Support Template" },
{ 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" },
{ 0x0101, 0, 0, 0, 0, 0, 0, "Private DO 1"},
{ 0x0102, 0, 0, 0, 0, 0, 0, "Private DO 2"},
{ 0x0103, 0, 0, 0, 0, 0, 0, "Private DO 3"},
{ 0x0104, 0, 0, 0, 0, 0, 0, "Private DO 4"},
{ 0x7F21, 1, 0, 1, 0, 0, 0, "Cardholder certificate"},
{ 0x005E, 0, 0, 1, 0, 0, 0, 0, "Login Data" },
{ 0x5F50, 0, 0, 0, 0, 0, 0, 0, "URL" },
{ 0x5F52, 0, 0, 1, 0, 0, 0, 0, "Historical Bytes" },
{ 0x0065, 1, 0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" },
{ 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Sex" },
{ 0x006E, 1, 0, 1, 0, 0, 0, 0, "Application Related Data" },
{ 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" },
{ 0x0073, 1, 0, 1, 0, 0, 0, 0, "Discretionary Data Objects" },
{ 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" },
{ 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" },
{ 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" },
{ 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" },
{ 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" },
{ 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" },
{ 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" },
{ 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" },
{ 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" },
{ 0x007A, 1, 0, 1, 0, 0, 0, 0, "Security Support Template" },
{ 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" },
{ 0x0101, 0, 0, 0, 0, 0, 0, 0, "Private DO 1"},
{ 0x0102, 0, 0, 0, 0, 0, 0, 0, "Private DO 2"},
{ 0x0103, 0, 0, 0, 0, 0, 0, 0, "Private DO 3"},
{ 0x0104, 0, 0, 0, 0, 0, 0, 0, "Private DO 4"},
{ 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"},
{ 0 }
};
@ -244,17 +246,19 @@ do_deinit (app_t app)
/* Wrapper around iso7816_get_data which first tries to get the data
from the cache. With GET_IMMEDIATE passed as true, the cache is
bypassed. */
bypassed. With TRY_EXTLEN extended lengths APDUs are use if
supported by the card. */
static gpg_error_t
get_cached_data (app_t app, int tag,
unsigned char **result, size_t *resultlen,
int get_immediate)
int get_immediate, int try_extlen)
{
gpg_error_t err;
int i;
unsigned char *p;
size_t len;
struct cache_s *c;
int exmode;
*result = NULL;
*resultlen = 0;
@ -279,7 +283,12 @@ get_cached_data (app_t app, int tag,
}
}
err = iso7816_get_data (app->slot, tag, &p, &len);
if (try_extlen && app->app_local->cardcap.ext_lc_le)
exmode = app->app_local->extcap.max_rsp_data;
else
exmode = 0;
err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
if (err)
return err;
*result = p;
@ -392,6 +401,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
unsigned char *value;
size_t valuelen;
int dummyrc;
int exmode;
if (!r_rc)
r_rc = &dummyrc;
@ -404,7 +414,11 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
{
rc = iso7816_get_data (app->slot, tag, &buffer, &buflen);
if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le)
exmode = app->app_local->extcap.max_rsp_data;
else
exmode = 0;
rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
if (rc)
{
*r_rc = rc;
@ -422,7 +436,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
rc = get_cached_data (app, data_objects[i].get_from,
&buffer, &buflen,
(data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11));
|| data_objects[i].get_immediate_in_v11),
data_objects[i].try_extlen);
if (!rc)
{
const unsigned char *s;
@ -445,7 +460,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
{
rc = get_cached_data (app, tag, &buffer, &buflen,
(data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11));
|| data_objects[i].get_immediate_in_v11),
data_objects[i].try_extlen);
if (!rc)
{
value = buffer;
@ -476,7 +492,9 @@ dump_all_do (int slot)
if (data_objects[i].get_from)
continue;
rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen);
/* We don't try extended length APDU because such large DO would
be pretty useless in a log file. */
rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
;
else if (rc)
@ -621,13 +639,14 @@ parse_login_data (app_t app)
/* Note, that FPR must be at least 20 bytes. */
static gpg_error_t
store_fpr (int slot, int keynumber, u32 timestamp,
store_fpr (app_t app, int keynumber, u32 timestamp,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen,
unsigned char *fpr, unsigned int card_version)
{
unsigned int n, nbits;
unsigned char *buffer, *p;
int tag, tag2;
int rc;
for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
@ -662,9 +681,12 @@ store_fpr (int slot, int keynumber, u32 timestamp,
xfree (buffer);
rc = iso7816_put_data (slot, 0,
(card_version > 0x0007? 0xC7 : 0xC6)
+ keynumber, fpr, 20);
tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
flush_cache_item (app, tag);
tag2 = 0xCE + keynumber;
flush_cache_item (app, tag2);
rc = iso7816_put_data (app->slot, 0, tag, fpr, 20);
if (rc)
log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
@ -677,7 +699,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
buf[2] = timestamp >> 8;
buf[3] = timestamp;
rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4);
rc = iso7816_put_data (app->slot, 0, tag2, buf, 4);
if (rc)
log_error (_("failed to store the creation date: %s\n"),
gpg_strerror (rc));
@ -2131,7 +2153,7 @@ does_key_exist (app_t app, int keyidx, int force)
assert (keyidx >=0 && keyidx <= 2);
if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen))
{
log_error (_("error reading application data\n"));
return gpg_error (GPG_ERR_GENERAL);
@ -2623,7 +2645,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave;
}
err = store_fpr (app->slot, keyno, created_at,
err = store_fpr (app, keyno, created_at,
rsa_n, rsa_n_len, rsa_e, rsa_e_len,
fprbuf, app->card_version);
if (err)
@ -2757,7 +2779,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
send_status_info (ctrl, "KEY-CREATED-AT",
numbuf, (size_t)strlen(numbuf), NULL, 0);
rc = store_fpr (app->slot, keyno, (u32)created_at,
rc = store_fpr (app, keyno, (u32)created_at,
m, mlen, e, elen, fprbuf, app->card_version);
if (rc)
goto leave;
@ -2811,7 +2833,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
assert (keyno >= 1 && keyno <= 3);
rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0);
rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
if (rc)
{
log_error (_("error reading application data\n"));
@ -3502,7 +3524,7 @@ app_select_openpgp (app_t app)
replace a possibly already set one from a EF.GDO with this
one. Note, that for current OpenPGP cards, no EF.GDO exists
and thus it won't matter at all. */
rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen);
if (rc)
goto leave;
if (opt.verbose)

View File

@ -420,19 +420,27 @@ iso7816_reset_retry_counter (int slot, int chvno,
a newly allocated buffer at the address passed by RESULT. Return
the length of this data at the address of RESULTLEN. */
gpg_error_t
iso7816_get_data (int slot, int tag,
iso7816_get_data (int slot, int extended_mode, int tag,
unsigned char **result, size_t *resultlen)
{
int sw;
int le;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send (slot, 0, 0x00, CMD_GET_DATA,
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
result, resultlen);
if (extended_mode > 0 && extended_mode < 256)
le = 65534; /* Not 65535 in case it is used as some special flag. */
else if (extended_mode > 0)
le = extended_mode;
else
le = 256;
sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA,
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le,
result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */

View File

@ -84,7 +84,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
const char *data,
size_t datalen);
gpg_error_t iso7816_get_data (int slot, int tag,
gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen);