aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2021-01-27 18:39:42 +0100
committerWerner Koch <wk@gnupg.org>2021-01-27 18:39:42 +0100
commit1e197c29ed95d021f5693cd3652b6acb07d928ea (patch)
tree32ebb057226a41466a43f5e45ded3b34ff793b8f
parent7620473cd007c074b0625a678caa6105a4c87142 (diff)
downloadgnupg-1e197c29ed95d021f5693cd3652b6acb07d928ea.tar.gz
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>
-rw-r--r--scd/app-p15.c97
1 files changed, 60 insertions, 37 deletions
diff --git a/scd/app-p15.c b/scd/app-p15.c
index c13921183..06de38670 100644
--- a/scd/app-p15.c
+++ b/scd/app-p15.c
@@ -42,6 +42,7 @@
#include "iso7816.h"
#include "../common/i18n.h"
#include "../common/tlv.h"
+#include "../common/host2net.h"
#include "apdu.h" /* fixme: we should move the card detection to a
separate file */
@@ -107,6 +108,8 @@ static struct
#define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \
|| (a)->app_local->card_type == CARD_TYPE_CARDOS_53)
+/* The default PKCS-15 home DF */
+#define DEFAULT_HOME_DF 0x5015
/* The AID of PKCS15. */
static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
@@ -381,6 +384,8 @@ struct app_local_s
/*** 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 readcert_by_cdf (app_t app, cdf_object_t cdf,
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
must free BUFFER only on success. */
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)
{
gpg_error_t err;
int sw;
- err = iso7816_select_file (slot, efid, 0);
+ err = select_ef_by_path (app, &efid, 1);
if (err)
{
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;
}
- 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)
log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n",
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
* of the EF. The caller must free BUFFER only on success. */
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,
unsigned char **buffer, size_t *buflen)
{
@@ -506,7 +512,7 @@ select_and_read_record (int slot, unsigned short efid, int recno,
if (efid)
{
- err = iso7816_select_file (slot, efid, 0);
+ err = select_ef_by_path (app, &efid, 1);
if (err)
{
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 (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 )
{
- path++;
- pathlen--;
+ if (pathlen == 1)
+ 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);
}
-
- /* CardOS 5 supports P0=0x09 to select stating at the CDF. */
- err = iso7816_select_path (app_get_slot (app), path, pathlen,
- IS_CARDOS_5(app));
+ else
+ err = iso7816_select_path (app_get_slot (app), path, pathlen,
+ app->app_local->home_df);
if (err)
{
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 )
log_error ("p15: warning: relative path select not yet implemented\n");
+ /* FIXME: Use home_df. */
for (i=0; i < pathlen; i++)
{
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;
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++)
log_printf ("%s%04hX", j? "/":"", path[j]);
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. */
{
- if (app->app_local->home_df)
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
(unsigned int)(app->app_local->home_df & 0xffff));
else
@@ -751,7 +765,7 @@ read_ef_odf (app_t app, unsigned short odf_fid)
size_t offset;
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);
if (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)
&& (!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
* ODF object. Here are sample values:
* 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. */
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);
else
- err = select_and_read_binary (app_get_slot (app), fid, "PrKDF",
+ err = select_and_read_binary (app, fid, "PrKDF",
&buffer, &buflen);
if (err)
return err;
@@ -1355,7 +1370,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
goto parse_error;
log_assert (objid);
- /* Skip subClassAttributes. */
+ /* Skip commonPrivateKeyAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&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))
{
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);
if (err) {
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. */
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);
else
- err = select_and_read_binary (app_get_slot (app), fid, "CDF",
+ err = select_and_read_binary (app, fid, "CDF",
&buffer, &buflen);
if (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))
{
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);
if (err) {
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. */
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);
else
- err = select_and_read_binary (app_get_slot (app), fid, "AODF",
+ err = select_and_read_binary (app, fid, "AODF",
&buffer, &buflen);
if (err)
return err;
@@ -1912,8 +1927,6 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
p = buffer;
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
starting with 0x00 or 0xff as these values are commonly used to
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))
{
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);
if (err) {
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->card_product = CARD_PRODUCT_UNKNOWN;
- err = select_and_read_binary (app_get_slot (app), 0x5032, "TokenInfo",
- &buffer, &buflen);
+ err = select_and_read_binary (app, 0x5032, "TokenInfo", &buffer, &buflen);
if (err)
return err;
@@ -2834,7 +2846,7 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
if (!buf)
return gpg_error_from_syserror ();
p = stpcpy (buf, "P15");
- if (app->app_local->home_df)
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
{
snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff));
@@ -3003,7 +3015,7 @@ keyref_from_prkdf (app_t app, prkdf_object_t prkdf)
if (!buf)
return NULL;
p = stpcpy (buf, "P15");
- if (app->app_local->home_df)
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
{
snprintf (p, 6, "-%04X",
(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;
int direct = 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)
{ /* Not found: Try to locate it from 2F00. We use direct path
selection here because it seems that the Belgian eID card
@@ -4452,7 +4467,7 @@ app_select_p15 (app_t app)
}
if (rc)
{ /* 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);
}
if (!rc)
@@ -4497,11 +4512,18 @@ app_select_p15 (app_t app)
goto leave;
}
- /* Set the home DF. Note that we currently can't do that if the
- selection via application ID worked. This will store 0 there
- instead. FIXME: We either need to figure the home_df via the
- DIR file or using the return values from the select file
- APDU. */
+ /* Set the home DF from the FCI returned by the select. */
+ if (!def_home_df && fci)
+ {
+ const unsigned char *s;
+ 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;
/* 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);
}
+ xfree (fci);
return rc;
}