scd:p15: Make file selection more robust.

* scd/app-p15.c: Include host2net.h.
(DEFAULT_HOME_DF): New.
(select_and_read_binary): Replace slot by app.  Change callers.  Use
select_ef_by_path.
(select_and_read_record): ditto.
(select_ef_by_path): Make use use the home_df.
(parse_certid): Adjust for always set home_df.
(print_tokeninfo_tokenflags): Ditto.
(app_select_p15): Take the home_df from the FCI returned by select.
--

This uses modern APDUs and always selectd starting at the PCKS-15 home
DF.  We could have made this much simpler but the goal is to keep
support for older cards although we can't test that easily.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-01-27 18:39:42 +01:00
parent 7620473cd0
commit 1e197c29ed
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 60 additions and 37 deletions

View File

@ -42,6 +42,7 @@
#include "iso7816.h" #include "iso7816.h"
#include "../common/i18n.h" #include "../common/i18n.h"
#include "../common/tlv.h" #include "../common/tlv.h"
#include "../common/host2net.h"
#include "apdu.h" /* fixme: we should move the card detection to a #include "apdu.h" /* fixme: we should move the card detection to a
separate file */ separate file */
@ -107,6 +108,8 @@ static struct
#define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \ #define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \
|| (a)->app_local->card_type == CARD_TYPE_CARDOS_53) || (a)->app_local->card_type == CARD_TYPE_CARDOS_53)
/* The default PKCS-15 home DF */
#define DEFAULT_HOME_DF 0x5015
/* The AID of PKCS15. */ /* The AID of PKCS15. */
static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63, static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
@ -381,6 +384,8 @@ struct app_local_s
/*** Local prototypes. ***/ /*** Local prototypes. ***/
static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path,
size_t pathlen);
static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf); static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf);
static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
unsigned char **r_cert, size_t *r_certlen); unsigned char **r_cert, size_t *r_certlen);
@ -470,13 +475,13 @@ do_deinit (app_t app)
BUFFER and BUFLEN contain the entire content of the EF. The caller BUFFER and BUFLEN contain the entire content of the EF. The caller
must free BUFFER only on success. */ must free BUFFER only on success. */
static gpg_error_t static gpg_error_t
select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, select_and_read_binary (app_t app, unsigned short efid, const char *efid_desc,
unsigned char **buffer, size_t *buflen) unsigned char **buffer, size_t *buflen)
{ {
gpg_error_t err; gpg_error_t err;
int sw; int sw;
err = iso7816_select_file (slot, efid, 0); err = select_ef_by_path (app, &efid, 1);
if (err) if (err)
{ {
log_error ("p15: error selecting %s (0x%04X): %s\n", log_error ("p15: error selecting %s (0x%04X): %s\n",
@ -484,7 +489,8 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
return err; return err;
} }
err = iso7816_read_binary_ext (slot, 0, 0, 0, buffer, buflen, &sw); err = iso7816_read_binary_ext (app_get_slot (app),
0, 0, 0, buffer, buflen, &sw);
if (err) if (err)
log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n", log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n",
efid_desc, efid, gpg_strerror (err), sw); efid_desc, efid, gpg_strerror (err), sw);
@ -497,7 +503,7 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
* messages. On success BUFFER and BUFLEN contain the entire content * messages. On success BUFFER and BUFLEN contain the entire content
* of the EF. The caller must free BUFFER only on success. */ * of the EF. The caller must free BUFFER only on success. */
static gpg_error_t static gpg_error_t
select_and_read_record (int slot, unsigned short efid, int recno, select_and_read_record (app_t app, unsigned short efid, int recno,
const char *efid_desc, const char *efid_desc,
unsigned char **buffer, size_t *buflen) unsigned char **buffer, size_t *buflen)
{ {
@ -506,7 +512,7 @@ select_and_read_record (int slot, unsigned short efid, int recno,
if (efid) if (efid)
{ {
err = iso7816_select_file (slot, efid, 0); err = select_ef_by_path (app, &efid, 1);
if (err) if (err)
{ {
log_error ("p15: error selecting %s (0x%04X): %s\n", log_error ("p15: error selecting %s (0x%04X): %s\n",
@ -515,7 +521,8 @@ select_and_read_record (int slot, unsigned short efid, int recno,
} }
} }
err = iso7816_read_record_ext (slot, recno, 1, 0, buffer, buflen, &sw); err = iso7816_read_record_ext (app_get_slot (app),
recno, 1, 0, buffer, buflen, &sw);
if (err) if (err)
{ {
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@ -554,13 +561,15 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
{ {
if (pathlen && *path == 0x3f00 ) if (pathlen && *path == 0x3f00 )
{ {
path++; if (pathlen == 1)
pathlen--; err = iso7816_select_mf (app_get_slot (app));
else
err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1,
app->app_local->home_df);
} }
else
/* CardOS 5 supports P0=0x09 to select stating at the CDF. */ err = iso7816_select_path (app_get_slot (app), path, pathlen,
err = iso7816_select_path (app_get_slot (app), path, pathlen, app->app_local->home_df);
IS_CARDOS_5(app));
if (err) if (err)
{ {
log_error ("p15: error selecting path "); log_error ("p15: error selecting path ");
@ -572,6 +581,7 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
if (pathlen && *path != 0x3f00 ) if (pathlen && *path != 0x3f00 )
log_error ("p15: warning: relative path select not yet implemented\n"); log_error ("p15: warning: relative path select not yet implemented\n");
/* FIXME: Use home_df. */
for (i=0; i < pathlen; i++) for (i=0; i < pathlen; i++)
{ {
err = iso7816_select_file (app_get_slot (app), err = iso7816_select_file (app_get_slot (app),
@ -586,6 +596,10 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
return 0; return 0;
err_print_path: err_print_path:
if (pathlen && *path == 0x3f00 )
log_printf ("3F00/");
else
log_printf ("%04hX/", app->app_local->home_df);
for (j=0; j < pathlen; j++) for (j=0; j < pathlen; j++)
log_printf ("%s%04hX", j? "/":"", path[j]); log_printf ("%s%04hX", j? "/":"", path[j]);
log_printf (": %s\n", gpg_strerror (err)); log_printf (": %s\n", gpg_strerror (err));
@ -629,7 +643,7 @@ parse_certid (app_t app, const char *certid,
} }
else /* This is a usual keyref. */ else /* This is a usual keyref. */
{ {
if (app->app_local->home_df) if (app->app_local->home_df != DEFAULT_HOME_DF)
snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.", snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
(unsigned int)(app->app_local->home_df & 0xffff)); (unsigned int)(app->app_local->home_df & 0xffff));
else else
@ -751,7 +765,7 @@ read_ef_odf (app_t app, unsigned short odf_fid)
size_t offset; size_t offset;
unsigned short home_df = 0; unsigned short home_df = 0;
err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF", err = select_and_read_binary (app, odf_fid, "ODF",
&buffer, &buflen); &buffer, &buflen);
if (err) if (err)
return err; return err;
@ -778,6 +792,7 @@ read_ef_odf (app_t app, unsigned short odf_fid)
&& !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7) && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
&& (!home_df || home_df == ((p[8]<<8)|p[9])) ) && (!home_df || home_df == ((p[8]<<8)|p[9])) )
{ {
/* FIXME: Is this hack still required? */
/* If we do not know the home DF, we take it from the first /* If we do not know the home DF, we take it from the first
* ODF object. Here are sample values: * ODF object. Here are sample values:
* a0 0a 30 08 0406 3f00 5015 4401 * a0 0a 30 08 0406 3f00 5015 4401
@ -1270,10 +1285,10 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */
if (IS_CARDOS_5 (app)) if (IS_CARDOS_5 (app))
err = select_and_read_record (app_get_slot (app), fid, recno, "PrKDF", err = select_and_read_record (app, fid, recno, "PrKDF",
&buffer, &buflen); &buffer, &buflen);
else else
err = select_and_read_binary (app_get_slot (app), fid, "PrKDF", err = select_and_read_binary (app, fid, "PrKDF",
&buffer, &buflen); &buffer, &buflen);
if (err) if (err)
return err; return err;
@ -1355,7 +1370,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
goto parse_error; goto parse_error;
log_assert (objid); log_assert (objid);
/* Skip subClassAttributes. */ /* Skip commonPrivateKeyAttributes. */
where = __LINE__; where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen); &ndef, &objlen, &hdrlen);
@ -1555,7 +1570,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
if (IS_CARDOS_5 (app)) if (IS_CARDOS_5 (app))
{ {
xfree (buffer); buffer = NULL; xfree (buffer); buffer = NULL;
err = select_and_read_record (app_get_slot (app), 0, recno, "PrKDF", err = select_and_read_record (app, 0, recno, "PrKDF",
&buffer, &buflen); &buffer, &buflen);
if (err) { if (err) {
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@ -1600,10 +1615,10 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */
if (IS_CARDOS_5 (app)) if (IS_CARDOS_5 (app))
err = select_and_read_record (app_get_slot (app), fid, recno, "CDF", err = select_and_read_record (app, fid, recno, "CDF",
&buffer, &buflen); &buffer, &buflen);
else else
err = select_and_read_binary (app_get_slot (app), fid, "CDF", err = select_and_read_binary (app, fid, "CDF",
&buffer, &buflen); &buffer, &buflen);
if (err) if (err)
return err; return err;
@ -1825,7 +1840,7 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
if (IS_CARDOS_5 (app)) if (IS_CARDOS_5 (app))
{ {
xfree (buffer); buffer = NULL; xfree (buffer); buffer = NULL;
err = select_and_read_record (app_get_slot (app), 0, recno, "CDF", err = select_and_read_record (app, 0, recno, "CDF",
&buffer, &buflen); &buffer, &buflen);
if (err) { if (err) {
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@ -1901,10 +1916,10 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */ return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */
if (IS_CARDOS_5 (app)) if (IS_CARDOS_5 (app))
err = select_and_read_record (app_get_slot (app), fid, recno, "AODF", err = select_and_read_record (app, fid, recno, "AODF",
&buffer, &buflen); &buffer, &buflen);
else else
err = select_and_read_binary (app_get_slot (app), fid, "AODF", err = select_and_read_binary (app, fid, "AODF",
&buffer, &buflen); &buffer, &buflen);
if (err) if (err)
return err; return err;
@ -1912,8 +1927,6 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
p = buffer; p = buffer;
n = buflen; n = buflen;
/* FIXME: This shares a LOT of code with read_ef_prkdf! */
/* Loop over the records. We stop as soon as we detect a new record /* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to starting with 0x00 or 0xff as these values are commonly used to
pad data blocks and are no valid ASN.1 encoding. Note the pad data blocks and are no valid ASN.1 encoding. Note the
@ -2456,7 +2469,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
if (IS_CARDOS_5 (app)) if (IS_CARDOS_5 (app))
{ {
xfree (buffer); buffer = NULL; xfree (buffer); buffer = NULL;
err = select_and_read_record (app_get_slot (app), 0, recno, "AODF", err = select_and_read_record (app, 0, recno, "AODF",
&buffer, &buflen); &buffer, &buflen);
if (err) { if (err) {
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@ -2601,8 +2614,7 @@ read_ef_tokeninfo (app_t app)
app->app_local->manufacturer_id = NULL; app->app_local->manufacturer_id = NULL;
app->app_local->card_product = CARD_PRODUCT_UNKNOWN; app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
err = select_and_read_binary (app_get_slot (app), 0x5032, "TokenInfo", err = select_and_read_binary (app, 0x5032, "TokenInfo", &buffer, &buflen);
&buffer, &buflen);
if (err) if (err)
return err; return err;
@ -2834,7 +2846,7 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
if (!buf) if (!buf)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
p = stpcpy (buf, "P15"); p = stpcpy (buf, "P15");
if (app->app_local->home_df) if (app->app_local->home_df != DEFAULT_HOME_DF)
{ {
snprintf (p, 6, "-%04X", snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff)); (unsigned int)(app->app_local->home_df & 0xffff));
@ -3003,7 +3015,7 @@ keyref_from_prkdf (app_t app, prkdf_object_t prkdf)
if (!buf) if (!buf)
return NULL; return NULL;
p = stpcpy (buf, "P15"); p = stpcpy (buf, "P15");
if (app->app_local->home_df) if (app->app_local->home_df != DEFAULT_HOME_DF)
{ {
snprintf (p, 6, "-%04X", snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff)); (unsigned int)(app->app_local->home_df & 0xffff));
@ -4426,8 +4438,11 @@ app_select_p15 (app_t app)
card_type_t card_type = CARD_TYPE_UNKNOWN; card_type_t card_type = CARD_TYPE_UNKNOWN;
int direct = 0; int direct = 0;
int is_belpic = 0; int is_belpic = 0;
unsigned char *fci = NULL;
size_t fcilen;
rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0); rc = iso7816_select_application_ext (slot, pkcs15_aid, sizeof pkcs15_aid, 1,
&fci, &fcilen);
if (rc) if (rc)
{ /* Not found: Try to locate it from 2F00. We use direct path { /* Not found: Try to locate it from 2F00. We use direct path
selection here because it seems that the Belgian eID card selection here because it seems that the Belgian eID card
@ -4452,7 +4467,7 @@ app_select_p15 (app_t app)
} }
if (rc) if (rc)
{ /* Still not found: Try the default DF. */ { /* Still not found: Try the default DF. */
def_home_df = 0x5015; def_home_df = DEFAULT_HOME_DF;
rc = iso7816_select_file (slot, def_home_df, 1); rc = iso7816_select_file (slot, def_home_df, 1);
} }
if (!rc) if (!rc)
@ -4497,11 +4512,18 @@ app_select_p15 (app_t app)
goto leave; goto leave;
} }
/* Set the home DF. Note that we currently can't do that if the /* Set the home DF from the FCI returned by the select. */
selection via application ID worked. This will store 0 there if (!def_home_df && fci)
instead. FIXME: We either need to figure the home_df via the {
DIR file or using the return values from the select file const unsigned char *s;
APDU. */ size_t n;
s = find_tlv (fci, fcilen, 0x83, &n);
if (s && n == 2)
def_home_df = buf16_to_ushort (s);
else
log_error ("p15: select(AID) did not return the DF\n");
}
app->app_local->home_df = def_home_df; app->app_local->home_df = def_home_df;
/* Store the card type. FIXME: We might want to put this into /* Store the card type. FIXME: We might want to put this into
@ -4575,5 +4597,6 @@ app_select_p15 (app_t app)
do_deinit (app); do_deinit (app);
} }
xfree (fci);
return rc; return rc;
} }