diff --git a/scd/ChangeLog b/scd/ChangeLog index 3aea9f08d..1db7a8a62 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,5 +1,18 @@ 2004-04-26 Werner Koch + * app-common.h: New members FNC.DEINIT and APP_LOCAL. + * app.c (release_application): Call new deconstructor. + * app-openpgp.c (do_deinit): New. + (get_cached_data, flush_cache_item, flush_cache_after_error) + (flush_cache): New. + (get_one_do): Replaced arg SLOT by APP. Make used of cached data. + (verify_chv2, verify_chv3): Flush some cache item after error. + (do_change_pin): Ditto. + (do_sign): Ditto. + (do_setattr): Flush cache item. + (do_genkey): Flush the entire cache. + (compare_fingerprint): Use cached data. + * scdaemon.c (main): Do the last change the usual way. This is so that we can easily test for versioned config files above. diff --git a/scd/app-common.h b/scd/app-common.h index c16a15719..6e62bf99d 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -23,6 +23,8 @@ #include +struct app_local_s; /* Defined by all app-*.c. */ + struct app_ctx_s { int initialized; /* The application has been initialied and the function pointers may be used. Note that for @@ -37,7 +39,9 @@ struct app_ctx_s { int force_chv1; /* True if the card does not cache CHV1. */ int did_chv2; int did_chv3; + struct app_local_s *app_local; /* Local to the application. */ struct { + void (*deinit) (app_t app); int (*learn_status) (app_t app, ctrl_t ctrl); int (*readcert) (app_t app, const char *certid, unsigned char **cert, size_t *certlen); @@ -75,7 +79,6 @@ struct app_ctx_s { void *pincb_arg); } fnc; - }; #if GNUPG_MAJOR_VERSION == 1 diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index af5f5a27f..7ecbc6a48 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -50,35 +50,199 @@ static struct { int constructed; int get_from; /* Constructed DO with this DO or 0 for direct access. */ int binary; + int dont_cache; + int flush_on_error; char *desc; } data_objects[] = { - { 0x005E, 0, 0, 1, "Login Data" }, - { 0x5F50, 0, 0, 0, "URL" }, - { 0x0065, 1, 0, 1, "Cardholder Related Data"}, - { 0x005B, 0, 0x65, 0, "Name" }, - { 0x5F2D, 0, 0x65, 0, "Language preferences" }, - { 0x5F35, 0, 0x65, 0, "Sex" }, - { 0x006E, 1, 0, 1, "Application Related Data" }, - { 0x004F, 0, 0x6E, 1, "AID" }, - { 0x0073, 1, 0, 1, "Discretionary Data Objects" }, - { 0x0047, 0, 0x6E, 1, "Card Capabilities" }, - { 0x00C0, 0, 0x6E, 1, "Extended Card Capabilities" }, - { 0x00C1, 0, 0x6E, 1, "Algorithm Attributes Signature" }, - { 0x00C2, 0, 0x6E, 1, "Algorithm Attributes Decryption" }, - { 0x00C3, 0, 0x6E, 1, "Algorithm Attributes Authentication" }, - { 0x00C4, 0, 0x6E, 1, "CHV Status Bytes" }, - { 0x00C5, 0, 0x6E, 1, "Fingerprints" }, - { 0x00C6, 0, 0x6E, 1, "CA Fingerprints" }, - { 0x007A, 1, 0, 1, "Security Support Template" }, - { 0x0093, 0, 0x7A, 1, "Digital Signature Counter" }, + { 0x005E, 0, 0, 1, 0, 0, "Login Data" }, + { 0x5F50, 0, 0, 0, 0, 0, "URL" }, + { 0x0065, 1, 0, 1, 0, 0, "Cardholder Related Data"}, + { 0x005B, 0, 0x65, 0, 0, 0, "Name" }, + { 0x5F2D, 0, 0x65, 0, 0, 0, "Language preferences" }, + { 0x5F35, 0, 0x65, 0, 0, 0, "Sex" }, + { 0x006E, 1, 0, 1, 0, 0, "Application Related Data" }, + { 0x004F, 0, 0x6E, 1, 0, 0, "AID" }, + { 0x0073, 1, 0, 1, 0, 0, "Discretionary Data Objects" }, + { 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" }, + { 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" }, + { 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" }, + { 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" }, + { 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" }, + { 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" }, + { 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" }, + { 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" }, + { 0x007A, 1, 0, 1, 0, 0, "Security Support Template" }, + { 0x0093, 0, 0x7A, 1, 1, 0, "Digital Signature Counter" }, { 0 } }; +struct cache_s { + struct cache_s *next; + int tag; + size_t length; + unsigned char data[1]; +}; + +struct app_local_s { + struct cache_s *cache; +}; + + static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen); static unsigned long get_sig_counter (APP app); +/* Deconstructor. */ +static void +do_deinit (app_t app) +{ + if (app && app->app_local) + { + struct cache_s *c, *c2; + + for (c = app->app_local->cache; c; c = c2) + { + c2 = c->next; + xfree (c); + } + xfree (app->app_local); + app->app_local = NULL; + } +} + + +/* Wrapper around iso7816_get_data which first tries to get the data + from the cache. */ +static gpg_error_t +get_cached_data (app_t app, int tag, + unsigned char **result, size_t *resultlen) +{ + gpg_error_t err; + int i; + unsigned char *p; + size_t len; + struct cache_s *c; + + + *result = NULL; + *resultlen = 0; + + if (app->app_local) + { + for (c=app->app_local->cache; c; c = c->next) + if (c->tag == tag) + { + p = xtrymalloc (c->length); + if (!p) + return gpg_error (gpg_err_code_from_errno (errno)); + memcpy (p, c->data, c->length); + *resultlen = c->length; + *result = p; + return 0; + } + } + + err = iso7816_get_data (app->slot, tag, &p, &len); + if (err) + return err; + *result = p; + *resultlen = len; + + /* Check whether we should cache this object. */ + for (i=0; data_objects[i].tag; i++) + if (data_objects[i].tag == tag) + { + if (data_objects[i].dont_cache) + return 0; + break; + } + + /* No, cache it. */ + if (!app->app_local) + app->app_local = xtrycalloc (1, sizeof *app->app_local); + + /* Note that we can safely ignore out of core errors. */ + if (app->app_local) + { + for (c=app->app_local->cache; c; c = c->next) + assert (c->tag != tag); + + c = xtrymalloc (sizeof *c + len); + if (c) + { + memcpy (c->data, p, len); + log_debug (" caching tag %04X\n", tag); + c->length = len; + c->tag = tag; + c->next = app->app_local->cache; + app->app_local->cache = c; + } + } + + return 0; +} + +/* Remove DO at TAG from the cache. */ +static void +flush_cache_item (app_t app, int tag) +{ + struct cache_s *c, *cprev; + int i; + + if (!app->app_local) + return; + + for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next) + if (c->tag == tag) + { + if (cprev) + cprev->next = c->next; + else + app->app_local->cache = c->next; + xfree (c); + + for (c=app->app_local->cache; c ; c = c->next) + assert (c->tag != tag); /* Oops: duplicated entry. */ + return; + } + + /* Try again if we have an outer tag. */ + for (i=0; data_objects[i].tag; i++) + if (data_objects[i].tag == tag && data_objects[i].get_from + && data_objects[i].get_from != tag) + flush_cache_item (app, data_objects[i].get_from); +} + +/* Flush all entries from the cache which might be out of sync after + an error. */ +static void +flush_cache_after_error (app_t app) +{ + int i; + + for (i=0; data_objects[i].tag; i++) + if (data_objects[i].flush_on_error) + flush_cache_item (app, data_objects[i].tag); +} + + +/* Flush the entire cache. */ +static void +flush_cache (app_t app) +{ + if (app && app->app_local) + { + struct cache_s *c, *c2; + + for (c = app->app_local->cache; c; c = c2) + { + c2 = c->next; + xfree (c); + } + app->app_local->cache = NULL; + } +} /* Get the DO identified by TAG from the card in SLOT and return a @@ -86,7 +250,7 @@ static unsigned long get_sig_counter (APP app); NULL if not found or a pointer which must be used to release the buffer holding value. */ static void * -get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) +get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) { int rc, i; unsigned char *buffer; @@ -103,8 +267,8 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) rc = -1; if (data_objects[i].tag && data_objects[i].get_from) { - rc = iso7816_get_data (slot, data_objects[i].get_from, - &buffer, &buflen); + rc = get_cached_data (app, data_objects[i].get_from, + &buffer, &buflen); if (!rc) { const unsigned char *s; @@ -125,7 +289,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) if (!value) /* Not in a constructed DO, try simple. */ { - rc = iso7816_get_data (slot, tag, &buffer, &buflen); + rc = get_cached_data (app, tag, &buffer, &buflen); if (!rc) { value = buffer; @@ -374,7 +538,7 @@ do_getattr (APP app, CTRL ctrl, const char *name) return 0; } - relptr = get_one_do (app->slot, table[idx].tag, &value, &valuelen); + relptr = get_one_do (app, table[idx].tag, &value, &valuelen); if (relptr) { if (table[idx].special == 1) @@ -429,7 +593,7 @@ do_learn_status (APP app, CTRL ctrl) /* Verify CHV2 if required. Depending on the configuration of the card CHV1 will also be verified. */ static int -verify_chv2 (APP app, +verify_chv2 (app_t app, int (*pincb)(void*, const char *, char **), void *pincb_arg) { @@ -458,6 +622,7 @@ verify_chv2 (APP app, { log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc)); xfree (pinvalue); + flush_cache_after_error (app); return rc; } app->did_chv2 = 1; @@ -471,6 +636,7 @@ verify_chv2 (APP app, { log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc)); xfree (pinvalue); + flush_cache_after_error (app); return rc; } app->did_chv1 = 1; @@ -517,6 +683,7 @@ verify_chv3 (APP app, if (rc) { log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc)); + flush_cache_after_error (app); return rc; } app->did_chv3 = 1; @@ -561,6 +728,10 @@ do_setattr (APP app, const char *name, if (rc) return rc; + /* Flush the cache before writing it, so that the next get operation + will reread the data from the card and thus get synced in case of + errors (e.g. data truncated by the card). */ + flush_cache_item (app, table[idx].tag); rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen); if (rc) log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc)); @@ -647,7 +818,8 @@ do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, pinvalue, strlen (pinvalue)); } xfree (pinvalue); - + if (rc) + flush_cache_after_error (app); leave: return rc; @@ -678,6 +850,10 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, return gpg_error (GPG_ERR_INV_ID); keyno--; + /* We flush the cache to increase the traffic before a key + generation. This _might_ help a card to gather more entropy. */ + flush_cache (app); + rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); if (rc) { @@ -711,6 +887,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, goto leave; xfree (buffer); buffer = NULL; + #if 1 log_info ("please wait while key is being generated ...\n"); start_at = time (NULL); @@ -801,7 +978,7 @@ get_sig_counter (APP app) size_t valuelen; unsigned long ul; - relptr = get_one_do (app->slot, 0x0093, &value, &valuelen); + relptr = get_one_do (app, 0x0093, &value, &valuelen); if (!relptr) return 0; ul = convert_sig_counter_value (value, valuelen); @@ -819,7 +996,7 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr) assert (keyno >= 1 && keyno <= 3); - rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); + rc = get_cached_data (app, 0x006E, &buffer, &buflen); if (rc) { log_error ("error reading application data\n"); @@ -979,6 +1156,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo, { log_error ("verify CHV1 failed\n"); xfree (pinvalue); + flush_cache_after_error (app); return rc; } app->did_chv1 = 1; @@ -992,6 +1170,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo, { log_error ("verify CHV2 failed\n"); xfree (pinvalue); + flush_cache_after_error (app); return rc; } app->did_chv2 = 1; @@ -1220,7 +1399,7 @@ app_select_openpgp (APP app) app->serialnolen = buflen; buffer = NULL; - relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen); + relptr = get_one_do (app, 0x00C4, &buffer, &buflen); if (!relptr) { log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n"); @@ -1232,6 +1411,7 @@ app_select_openpgp (APP app) if (opt.verbose > 1) dump_all_do (slot); + app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; app->fnc.readcert = NULL; app->fnc.getattr = do_getattr; @@ -1285,7 +1465,7 @@ app_openpgp_cardinfo (APP app, if (disp_name) { *disp_name = NULL; - relptr = get_one_do (app->slot, 0x005B, &value, &valuelen); + relptr = get_one_do (app, 0x005B, &value, &valuelen); if (relptr) { *disp_name = make_printable_string (value, valuelen, 0); @@ -1296,7 +1476,7 @@ app_openpgp_cardinfo (APP app, if (pubkey_url) { *pubkey_url = NULL; - relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen); + relptr = get_one_do (app, 0x5F50, &value, &valuelen); if (relptr) { *pubkey_url = make_printable_string (value, valuelen, 0); @@ -1310,7 +1490,7 @@ app_openpgp_cardinfo (APP app, *fpr2 = NULL; if (fpr3) *fpr3 = NULL; - relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen); + relptr = get_one_do (app, 0x00C5, &value, &valuelen); if (relptr && valuelen >= 60) { if (fpr1) diff --git a/scd/app.c b/scd/app.c index d395fe559..a9a9243eb 100644 --- a/scd/app.c +++ b/scd/app.c @@ -116,6 +116,12 @@ release_application (app_t app) if (!app) return; + if (app->fnc.deinit) + { + app->fnc.deinit (app); + app->fnc.deinit = NULL; + } + xfree (app->serialno); xfree (app); }