diff options
Diffstat (limited to 'scd')
-rw-r--r-- | scd/apdu.c | 27 | ||||
-rw-r--r-- | scd/app-common.h | 187 | ||||
-rw-r--r-- | scd/app-dinsig.c | 29 | ||||
-rw-r--r-- | scd/app-geldkarte.c | 18 | ||||
-rw-r--r-- | scd/app-help.c | 46 | ||||
-rw-r--r-- | scd/app-nks.c | 78 | ||||
-rw-r--r-- | scd/app-openpgp.c | 515 | ||||
-rw-r--r-- | scd/app-p15.c | 84 | ||||
-rw-r--r-- | scd/app-piv.c | 273 | ||||
-rw-r--r-- | scd/app-sc-hsm.c | 48 | ||||
-rw-r--r-- | scd/app.c | 1320 | ||||
-rw-r--r-- | scd/ccid-driver.c | 187 | ||||
-rw-r--r-- | scd/command.c | 360 | ||||
-rw-r--r-- | scd/iso7816.c | 17 | ||||
-rw-r--r-- | scd/scdaemon.c | 13 | ||||
-rw-r--r-- | scd/scdaemon.h | 19 |
16 files changed, 2176 insertions, 1045 deletions
diff --git a/scd/apdu.c b/scd/apdu.c index 816938ac5..2df113c5e 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -42,23 +42,11 @@ #include "rapdu.h" #endif /*USE_G10CODE_RAPDU*/ -#if defined(GNUPG_SCD_MAIN_HEADER) -#include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 -/* This is used with GnuPG version < 1.9. The code has been source - copied from the current GnuPG >= 1.9 and is maintained over - there. */ -#include "../common/options.h" -#include "errors.h" -#include "memory.h" -#include "../common/util.h" -#include "../common/i18n.h" -#include "dynload.h" -#include "cardglue.h" -#else /* GNUPG_MAJOR_VERSION != 1 */ -#include "scdaemon.h" -#include "../common/exechelp.h" -#endif /* GNUPG_MAJOR_VERSION != 1 */ +#if defined(GNUPG_MAJOR_VERSION) +# include "scdaemon.h" +# include "../common/exechelp.h" +#endif /*GNUPG_MAJOR_VERSION*/ + #include "../common/host2net.h" #include "iso7816.h" @@ -266,8 +254,13 @@ static npth_mutex_t reader_table_lock; struct pcsc_io_request_s { +#if defined(_WIN32) || defined(__CYGWIN__) + pcsc_dword_t protocol; + pcsc_dword_t pci_len; +#else unsigned long protocol; unsigned long pci_len; +#endif }; typedef struct pcsc_io_request_s *pcsc_io_request_t; diff --git a/scd/app-common.h b/scd/app-common.h index 3df896228..5866c9b32 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -36,35 +36,95 @@ /* Flags used with app_writekey. */ #define APP_WRITEKEY_FLAG_FORCE 1 /* Force overwriting existing key. */ +/* Flags used with app_readkey. */ +#define APP_READKEY_FLAG_INFO 1 /* Send also a KEYPAIRINFO line. */ + /* Bit flags set by the decipher function into R_INFO. */ #define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */ +/* List of supported card types. Generic is the usual ISO7817-4 + * compliant card. More specific card or token versions can be given + * here. Use strcardtype() to map them to a string. */ +typedef enum + { + CARDTYPE_GENERIC = 0, + CARDTYPE_YUBIKEY + + } cardtype_t; + +/* List of supported card applications. The source code for each + * application can usually be found in an app-NAME.c file. Use + * strapptype() to map them to a string. */ +typedef enum + { + APPTYPE_NONE = 0, + APPTYPE_UNDEFINED, + APPTYPE_OPENPGP, + APPTYPE_PIV, + APPTYPE_NKS, + APPTYPE_P15, + APPTYPE_GELDKARTE, + APPTYPE_DINSIG, + APPTYPE_SC_HSM + } apptype_t; + + +/* Forward declarations. */ +struct card_ctx_s; +struct app_ctx_s; struct app_local_s; /* Defined by all app-*.c. */ -struct app_ctx_s { - struct app_ctx_s *next; + +typedef struct card_ctx_s *card_t; +typedef struct app_ctx_s *app_t; + +/* The object describing a card. */ +struct card_ctx_s { + card_t next; npth_mutex_t lock; - /* Number of connections currently using this application context. - If this is not 0 the application has been initialized and the - function pointers may be used. Note that for unsupported - operations the particular function pointer is set to NULL */ + /* Number of connections currently using this application context. */ unsigned int ref_count; /* Used reader slot. */ int slot; - unsigned char *serialno; /* Serialnumber in raw form, allocated. */ - size_t serialnolen; /* Length in octets of serialnumber. */ - const char *cardtype; /* NULL or string with the token's type. */ - const char *apptype; + cardtype_t cardtype; /* The token's type. */ unsigned int cardversion;/* Firmware version of the token or 0. */ - unsigned int appversion; /* Version of the application or 0. */ + unsigned int card_status; + + /* The serial number is associated with the card and not with a + * specific app. If a card uses different serial numbers for its + * applications, our code picks the serial number of a specific + * application and uses that. */ + unsigned char *serialno; /* Serialnumber in raw form, allocated. */ + size_t serialnolen; /* Length in octets of serialnumber. */ + + /* A linked list of applications used on this card. The app at the + * head of the list is the currently active app; To work with the + * other apps, switching to that app might be needed. Switching will + * put the active app at the head of the list. */ + app_t app; + + /* Various flags. */ unsigned int reset_requested:1; unsigned int periodical_check_needed:1; +}; + + +/* The object describing a card's applications. A card may have + * several applications and it is usuallay required to explicity + * switch between applications. */ +struct app_ctx_s { + app_t next; + + card_t card; /* Link back to the card. */ + + apptype_t apptype; /* The type of the application. */ + unsigned int appversion; /* Version of the application or 0. */ unsigned int did_chv1:1; unsigned int force_chv1:1; /* True if the card does not cache CHV1. */ unsigned int did_chv2:1; @@ -72,11 +132,13 @@ struct app_ctx_s { struct app_local_s *app_local; /* Local to the application. */ struct { void (*deinit) (app_t app); + gpg_error_t (*reselect) (app_t app, ctrl_t ctrl); gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags); gpg_error_t (*readcert) (app_t app, const char *certid, unsigned char **cert, size_t *certlen); - gpg_error_t (*readkey) (app_t app, const char *certid, - unsigned char **pk, size_t *pklen); + gpg_error_t (*readkey) (app_t app, ctrl_t ctrl, + const char *certid, unsigned int flags, + unsigned char **pk, size_t *pklen); gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name); gpg_error_t (*setattr) (app_t app, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), @@ -121,11 +183,35 @@ struct app_ctx_s { gpg_error_t (*check_pin) (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); + gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action, + const char *keygrip_str); } fnc; }; + +/* Action values for app_do_with_keygrip. */ +enum + { + KEYGRIP_ACTION_SEND_DATA, + KEYGRIP_ACTION_WRITE_STATUS, + KEYGRIP_ACTION_LOOKUP + }; + + +/* Helper to get the slot from an APP object. */ +static inline int +app_get_slot (app_t app) +{ + if (app && app->card) + return app->card->slot; + return -1; +} + + /*-- app-help.c --*/ unsigned int app_help_count_bits (const unsigned char *a, size_t len); +gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen, + char *hexkeygrip); gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen, unsigned char **r_pk, size_t *r_pklen); @@ -133,70 +219,85 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ -void app_send_card_list (ctrl_t ctrl); +const char *strcardtype (cardtype_t t); +const char *strapptype (apptype_t t); + +void app_update_priority_list (const char *arg); +gpg_error_t app_send_card_list (ctrl_t ctrl); +char *card_get_serialno (card_t card); char *app_get_serialno (app_t app); void app_dump_state (void); void application_notify_card_reset (int slot); -gpg_error_t check_application_conflict (const char *name, app_t app); -gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset); -gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t *r_app, +gpg_error_t check_application_conflict (card_t card, const char *name, + const unsigned char *serialno_bin, + size_t serialno_bin_len); +gpg_error_t card_reset (card_t card, ctrl_t ctrl, int send_reset); +gpg_error_t select_application (ctrl_t ctrl, const char *name, card_t *r_app, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len); +gpg_error_t select_additional_application (ctrl_t ctrl, const char *name); char *get_supported_applications (void); -void release_application (app_t app, int locked_already); -gpg_error_t app_munge_serialno (app_t app); -gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl, + +card_t card_ref (card_t card); +void card_unref (card_t card); +void card_unref_locked (card_t card); + +gpg_error_t app_munge_serialno (card_t card); +gpg_error_t app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags); -gpg_error_t app_readcert (app_t app, ctrl_t ctrl, const char *certid, +gpg_error_t app_readcert (card_t card, ctrl_t ctrl, const char *certid, unsigned char **cert, size_t *certlen); -gpg_error_t app_readkey (app_t app, ctrl_t ctrl, - const char *keyid, unsigned char **pk, size_t *pklen); -gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name); -gpg_error_t app_setattr (app_t app, ctrl_t ctrl, const char *name, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - const unsigned char *value, size_t valuelen); -gpg_error_t app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - const void *indata, size_t indatalen, - unsigned char **outdata, size_t *outdatalen ); -gpg_error_t app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, +gpg_error_t app_readkey (card_t card, ctrl_t ctrl, + const char *keyid, unsigned int flags, + unsigned char **pk, size_t *pklen); +gpg_error_t app_getattr (card_t card, ctrl_t ctrl, const char *name); +gpg_error_t app_setattr (card_t card, ctrl_t ctrl, const char *name, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen); +gpg_error_t app_sign (card_t card, ctrl_t ctrl, + const char *keyidstr, int hashalgo, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); +gpg_error_t app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); -gpg_error_t app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, +gpg_error_t app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info); -gpg_error_t app_writecert (app_t app, ctrl_t ctrl, +gpg_error_t app_writecert (card_t card, ctrl_t ctrl, const char *certidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen); -gpg_error_t app_writekey (app_t app, ctrl_t ctrl, +gpg_error_t app_writekey (card_t card, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen); -gpg_error_t app_genkey (app_t app, ctrl_t ctrl, +gpg_error_t app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -gpg_error_t app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, +gpg_error_t app_get_challenge (card_t card, ctrl_t ctrl, size_t nbytes, unsigned char *buffer); -gpg_error_t app_change_pin (app_t app, ctrl_t ctrl, +gpg_error_t app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -gpg_error_t app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg); +gpg_error_t app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg); +card_t app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str); /*-- app-openpgp.c --*/ diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 983bed6e1..9993b68ff 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -81,7 +81,6 @@ #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" @@ -101,7 +100,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) /* Return the certificate of the card holder. */ fid = 0xC000; - len = app_help_read_length_of_cert (app->slot, fid, &certoff); + len = app_help_read_length_of_cert (app_get_slot (app), fid, &certoff); if (!len) return 0; /* Card has not been personalized. */ @@ -114,7 +113,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) /* Now we need to read the certificate, so that we can get the public key out of it. */ - err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen); + err = iso7816_read_binary (app_get_slot (app), certoff, len-certoff, + &der, &derlen); if (err) { log_info ("error reading entire certificate from FID 0x%04X: %s\n", @@ -193,14 +193,14 @@ do_readcert (app_t app, const char *certid, /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) { log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", @@ -293,7 +293,7 @@ verify_pin (app_t app, pininfo.maxlen = 8; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, _("||Please enter your PIN at the reader's pinpad"), @@ -304,7 +304,7 @@ verify_pin (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x81, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } @@ -345,7 +345,8 @@ verify_pin (app_t app, return gpg_error (GPG_ERR_BAD_PIN); } - rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app_get_slot (app), 0x81, + pinvalue, strlen (pinvalue)); if (gpg_err_code (rc) == GPG_ERR_INV_VALUE) { /* We assume that ISO 9564-1 encoding is used and we failed @@ -366,7 +367,8 @@ verify_pin (app_t app, paddedpin[i++] = (((*s - '0') << 4) | 0x0f); while (i < sizeof paddedpin) paddedpin[i++] = 0xff; - rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin); + rc = iso7816_verify (app_get_slot (app), 0x81, + paddedpin, sizeof paddedpin); } xfree (pinvalue); } @@ -482,7 +484,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, rc = verify_pin (app, pincb, pincb_arg); if (!rc) - rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, + rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, outdata, outdatalen); return rc; } @@ -532,7 +534,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, return err; } - err = iso7816_change_reference_data (app->slot, 0x81, + err = iso7816_change_reference_data (app_get_slot (app), 0x81, oldpin, oldpinlen, pinvalue, strlen (pinvalue)); xfree (pinvalue); @@ -547,14 +549,15 @@ gpg_error_t app_select_dinsig (app_t app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, aid, sizeof aid, 0); if (!rc) { - app->apptype = "DINSIG"; + app->apptype = APPTYPE_DINSIG; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = NULL; diff --git a/scd/app-geldkarte.c b/scd/app-geldkarte.c index 85bcedc4f..141985932 100644 --- a/scd/app-geldkarte.c +++ b/scd/app-geldkarte.c @@ -39,7 +39,6 @@ #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" @@ -277,7 +276,7 @@ app_select_geldkarte (app_t app) static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x25, 0x45, 0x50, 0x02, 0x00 }; gpg_error_t err; - int slot = app->slot; + int slot = app_get_slot (app); unsigned char *result = NULL; size_t resultlen; struct app_local_s *ld; @@ -312,21 +311,22 @@ app_select_geldkarte (app_t app) goto leave; /* Probably not a Geldkarte. */ } - app->apptype = "GELDKARTE"; + app->apptype = APPTYPE_GELDKARTE; app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; /* If we don't have a serialno yet construct it from the EF_ID. */ - if (!app->serialno) + if (!app->card->serialno) { - app->serialno = xtrymalloc (10); - if (!app->serialno) + app->card->serialno = xtrymalloc (10); + if (!app->card->serialno) { err = gpg_error_from_syserror (); goto leave; } - memcpy (app->serialno, result, 10); - app->serialnolen = 10; - err = app_munge_serialno (app); + memcpy (app->card->serialno, result, 10); + app->card->serialnolen = 10; + err = app_munge_serialno (app->card); if (err) goto leave; } diff --git a/scd/app-help.c b/scd/app-help.c index f0f551c55..e3ad74434 100644 --- a/scd/app-help.c +++ b/scd/app-help.c @@ -24,7 +24,6 @@ #include <string.h> #include "scdaemon.h" -#include "app-common.h" #include "iso7816.h" #include "../common/tlv.h" @@ -52,26 +51,17 @@ app_help_count_bits (const unsigned char *a, size_t len) } -/* Return the KEYGRIP for the certificate CERT as an hex encoded - string in the user provided buffer HEXKEYGRIP which must be of at - least 41 bytes. */ +/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN) + * as an hex encoded string in the user provided buffer HEXKEYGRIP + * which must be of at least 41 bytes. */ gpg_error_t -app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) +app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip) { gpg_error_t err; gcry_sexp_t s_pkey; - ksba_sexp_t p; - size_t n; - unsigned char array[20]; + unsigned char array[KEYGRIP_LEN]; - p = ksba_cert_get_public_key (cert); - if (!p) - return gpg_error (GPG_ERR_BUG); - n = gcry_sexp_canon_len (p, 0, NULL, NULL); - if (!n) - return gpg_error (GPG_ERR_INV_SEXP); - err = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n); - xfree (p); + err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen); if (err) return err; /* Can't parse that S-expression. */ if (!gcry_pk_get_keygrip (s_pkey, array)) @@ -81,12 +71,34 @@ app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) } gcry_sexp_release (s_pkey); - bin2hex (array, 20, hexkeygrip); + bin2hex (array, KEYGRIP_LEN, hexkeygrip); return 0; } +/* Return the KEYGRIP for the certificate CERT as an hex encoded + string in the user provided buffer HEXKEYGRIP which must be of at + least 41 bytes. */ +gpg_error_t +app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) +{ + gpg_error_t err; + ksba_sexp_t p; + size_t n; + + p = ksba_cert_get_public_key (cert); + if (!p) + return gpg_error (GPG_ERR_BUG); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip); + ksba_free (p); + return err; +} + + gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen, unsigned char **r_pk, size_t *r_pklen) diff --git a/scd/app-nks.c b/scd/app-nks.c index 40c941616..d12720cf6 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -53,7 +53,6 @@ #include "scdaemon.h" #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "apdu.h" #include "../common/host2net.h" @@ -151,13 +150,15 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) int i; int offset[2] = { 0, 0 }; - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) return err; - err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]); + err = iso7816_read_record (app_get_slot (app), 1, 1, 0, + &buffer[0], &buflen[0]); if (err) return err; - err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]); + err = iso7816_read_record (app_get_slot (app), 2, 1, 0, + &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); @@ -272,7 +273,7 @@ get_chv_status (app_t app, int sigg, int pwid) command[2] = 0x00; command[3] = pwid; - if (apdu_send_direct (app->slot, 0, (unsigned char *)command, + if (apdu_send_direct (app_get_slot (app), 0, (unsigned char *)command, 4, 0, NULL, &result, &resultlen)) rc = -1; /* Error. */ else if (resultlen < 2) @@ -406,7 +407,7 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) { size_t len; - len = app_help_read_length_of_cert (app->slot, + len = app_help_read_length_of_cert (app_get_slot (app), filelist[i].fid, NULL); if (len) { @@ -528,14 +529,14 @@ do_readcert (app_t app, const char *certid, /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) { log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", @@ -618,7 +619,8 @@ do_readcert (app_t app, const char *certid, certificate parsing code in commands.c:cmd_readkey. For internal use PK and PKLEN may be NULL to just check for an existing key. */ static gpg_error_t -do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) +do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, + unsigned char **pk, size_t *pklen) { gpg_error_t err; unsigned char *buffer[2]; @@ -632,13 +634,14 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* Access the KEYD file which is always in the master directory. */ - err = iso7816_select_path (app->slot, path, DIM (path)); + err = iso7816_select_path (app_get_slot (app), path, DIM (path)); if (err) return err; /* Due to the above select we need to re-select our application. */ app->app_local->need_app_select = 1; /* Get the two records. */ - err = iso7816_read_record (app->slot, 5, 1, 0, &buffer[0], &buflen[0]); + err = iso7816_read_record (app_get_slot (app), 5, 1, 0, + &buffer[0], &buflen[0]); if (err) return err; if (all_zero_p (buffer[0], buflen[0])) @@ -646,13 +649,22 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) xfree (buffer[0]); return gpg_error (GPG_ERR_NOT_FOUND); } - err = iso7816_read_record (app->slot, 6, 1, 0, &buffer[1], &buflen[1]); + err = iso7816_read_record (app_get_slot (app), 6, 1, 0, + &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); return err; } + if ((flags & APP_READKEY_FLAG_INFO)) + { + /* Not yet implemented but we won't get here for any regular + * keyrefs anyway, thus the top layer will provide the + * keypairinfo from the certificate. */ + (void)ctrl; + } + if (pk && pklen) { *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0], @@ -698,7 +710,7 @@ do_writekey (app_t app, ctrl_t ctrl, else return gpg_error (GPG_ERR_INV_ID); - if (!force && !do_readkey (app, keyid, NULL, NULL)) + if (!force && !do_readkey (app, ctrl, keyid, 0, NULL, NULL)) return gpg_error (GPG_ERR_EEXIST); /* Parse the S-expression. */ @@ -751,7 +763,7 @@ do_writekey (app_t app, ctrl_t ctrl, /* mse[10] = 0x82; /\* RSA public exponent of up to 4 bytes. *\/ */ /* mse[12] = rsa_e_len; */ /* memcpy (mse+12, rsa_e, rsa_e_len); */ -/* err = iso7816_manage_security_env (app->slot, 0x81, 0xB6, */ +/* err = iso7816_manage_security_env (app_get_slot (app), 0x81, 0xB6, */ /* mse, sizeof mse); */ leave: @@ -794,7 +806,7 @@ verify_pin (app_t app, int pwid, const char *desc, pininfo.maxlen = 16; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, desc, NULL); if (rc) @@ -804,7 +816,7 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify_kp (app->slot, pwid, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), pwid, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else @@ -825,7 +837,8 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app_get_slot (app), pwid, + pinvalue, strlen (pinvalue)); xfree (pinvalue); } @@ -963,7 +976,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; - rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6, + rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } /* Verify using PW1.CH. */ @@ -971,7 +984,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, rc = verify_pin (app, 0, NULL, pincb, pincb_arg); /* Compute the signature. */ if (!rc) - rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, + rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, outdata, outdatalen); return rc; } @@ -1038,7 +1051,7 @@ do_decipher (app_t app, const char *keyidstr, mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; - rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8, + rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, mse, sizeof mse); } else @@ -1048,7 +1061,7 @@ do_decipher (app_t app, const char *keyidstr, 0x80, 1, 0x10, /* Select algorithm RSA. */ 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ }; - rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8, + rc = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8, mse, sizeof mse); } @@ -1059,7 +1072,8 @@ do_decipher (app_t app, const char *keyidstr, /* Note that we need to use extended length APDUs for TCOS 3 cards. Command chaining does not work. */ if (!rc) - rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0, + rc = iso7816_decipher (app_get_slot (app), + app->app_local->nks_version > 2? 1:0, indata, indatalen, 0, 0x81, outdata, outdatalen); return rc; @@ -1251,13 +1265,13 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, } memcpy (data, oldpin, oldpinlen); memcpy (data+oldpinlen, newpin, newpinlen); - err = iso7816_reset_retry_counter_with_rc (app->slot, pwid, + err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), pwid, data, datalen); wipememory (data, datalen); xfree (data); } else - err = iso7816_change_reference_data (app->slot, pwid, + err = iso7816_change_reference_data (app_get_slot (app), pwid, oldpin, oldpinlen, newpin, newpinlen); leave: @@ -1338,9 +1352,11 @@ switch_application (app_t app, int enable_sigg) log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS"); if (enable_sigg) - err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0); + err = iso7816_select_application (app_get_slot (app), + aid_sigg, sizeof aid_sigg, 0); else - err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0); + err = iso7816_select_application (app_get_slot (app), + aid_nks, sizeof aid_nks, 0); if (!err && enable_sigg && app->app_local->nks_version >= 3 && !app->app_local->sigg_msig_checked) @@ -1353,9 +1369,10 @@ switch_application (app_t app, int enable_sigg) app->app_local->sigg_msig_checked = 1; app->app_local->sigg_is_msig = 1; - err = iso7816_select_file (app->slot, 0x5349, 0); + err = iso7816_select_file (app_get_slot (app), 0x5349, 0); if (!err) - err = iso7816_read_record (app->slot, 1, 1, 0, &buffer, &buflen); + err = iso7816_read_record (app_get_slot (app), 1, 1, 0, + &buffer, &buflen); if (!err) { tmpl = find_tlv (buffer, buflen, 0x7a, &tmpllen); @@ -1387,13 +1404,13 @@ switch_application (app_t app, int enable_sigg) gpg_error_t app_select_nks (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0); if (!rc) { - app->apptype = "NKS"; + app->apptype = APPTYPE_NKS; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -1407,6 +1424,7 @@ app_select_nks (app_t app) log_info ("Detected NKS version: %d\n", app->app_local->nks_version); app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 1e904b578..767f29d26 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -50,27 +50,20 @@ #include <assert.h> #include <time.h> -#if GNUPG_MAJOR_VERSION == 1 -/* This is used with GnuPG version < 1.9. The code has been source - copied from the current GnuPG >= 1.9 and is maintained over - there. */ -#include "options.h" -#include "errors.h" -#include "memory.h" -#include "cardglue.h" -#else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" -#endif /* GNUPG_MAJOR_VERSION != 1 */ - #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "../common/host2net.h" #include "../common/openpgpdefs.h" + +/* The AID of this application. */ +static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; + + /* A table describing the DOs of the card. */ static struct { int tag; @@ -176,6 +169,7 @@ struct app_local_s { is usually only required for cross checks because the length of an S-expression is implicitly available. */ + unsigned char keygrip_str[41]; /* The keygrip, null terminated */ } pk[3]; unsigned char status_indicator; /* The card status indicator. */ @@ -348,7 +342,7 @@ get_cached_data (app_t app, int tag, else exmode = 0; - err = iso7816_get_data (app->slot, exmode, tag, &p, &len); + err = iso7816_get_data (app_get_slot (app), exmode, tag, &p, &len); if (err) return err; if (len) @@ -479,7 +473,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, if (app->appversion > 0x0100 && data_objects[i].get_immediate_in_v11) { exmode = 0; - rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen); + rc = iso7816_get_data (app_get_slot (app), exmode, tag, &buffer, &buflen); if (rc) { *r_rc = rc; @@ -821,7 +815,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, tag2 = 0xCE + keynumber; flush_cache_item (app, 0xCD); - rc = iso7816_put_data (app->slot, 0, tag, fpr, 20); + rc = iso7816_put_data (app_get_slot (app), 0, tag, fpr, 20); if (rc) log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); @@ -834,7 +828,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, buf[2] = timestamp >> 8; buf[3] = timestamp; - rc = iso7816_put_data (app->slot, 0, tag2, buf, 4); + rc = iso7816_put_data (app_get_slot (app), 0, tag2, buf, 4); if (rc) log_error (_("failed to store the creation date: %s\n"), gpg_strerror (rc)); @@ -987,6 +981,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "PRIVATE-DO-3", 0x0103 }, { "PRIVATE-DO-4", 0x0104 }, { "$AUTHKEYID", 0x0000, -3 }, + { "$ENCRKEYID", 0x0000, -6 }, + { "$SIGNKEYID", 0x0000, -7 }, { "$DISPSERIALNO",0x0000, -4 }, { "UIF-1", 0x00D6, 0 }, { "UIF-2", 0x00D7, 0 }, @@ -1071,6 +1067,18 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) send_key_attr (ctrl, app, table[idx].name, i); return 0; } + if (table[idx].special == -6) + { + char const tmp[] = "OPENPGP.2"; + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + return 0; + } + if (table[idx].special == -7) + { + char const tmp[] = "OPENPGP.1"; + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + return 0; + } relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc); if (relptr) @@ -1214,7 +1222,6 @@ get_remaining_tries (app_t app, int adminpw) the according hex representation to FPR. Caller must have provide a buffer at FPR of least 41 bytes. Returns 0 on success or an error code. */ -#if GNUPG_MAJOR_VERSION > 1 static gpg_error_t retrieve_fpr_from_card (app_t app, int keyno, char *fpr) { @@ -1233,7 +1240,6 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr) xfree (relptr); return err; } -#endif /*GNUPG_MAJOR_VERSION > 1*/ /* Retrieve the public key material for the RSA key, whose fingerprint @@ -1242,7 +1248,6 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr) public exponent at E and ELEN. Returns zero on success, an error code on failure. Caller must release the allocated buffers at M and E if the function returns success. */ -#if GNUPG_MAJOR_VERSION > 1 static gpg_error_t retrieve_key_material (FILE *fp, const char *hexkeyid, const unsigned char **m, size_t *mlen, @@ -1347,7 +1352,6 @@ retrieve_key_material (FILE *fp, const char *hexkeyid, xfree (line); return err; } -#endif /*GNUPG_MAJOR_VERSION > 1*/ static gpg_error_t @@ -1561,6 +1565,23 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, } +static gpg_error_t +store_keygrip (app_t app, int keyno) +{ + gpg_error_t err; + unsigned char grip[20]; + + err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key, + app->app_local->pk[keyno].keylen, + grip); + if (err) + return err; + + bin2hex (grip, 20, app->app_local->pk[keyno].keygrip_str); + return 0; +} + + /* Parse tag-length-value data for public key in BUFFER of BUFLEN length. Key of KEYNO in APP is updated with an S-expression of public key. When CTRL is not NULL, fingerprint is computed with @@ -1612,6 +1633,8 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, app->app_local->pk[keyno].key = keybuf; /* Decrement for trailing '\0' */ app->app_local->pk[keyno].keylen = len - 1; + + err = store_keygrip (app, keyno); } return err; @@ -1628,7 +1651,6 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, consuming to send it just for the fun of it. However, given that we use the same code in gpg 1.4, we can't use the gcry S-expression here but need to open encode it. */ -#if GNUPG_MAJOR_VERSION > 1 static gpg_error_t get_public_key (app_t app, int keyno) { @@ -1673,7 +1695,7 @@ get_public_key (app_t app, int keyno) le_value = 256; /* Use legacy value. */ } - err = iso7816_read_public_key (app->slot, exmode, + err = iso7816_read_public_key (app_get_slot (app), exmode, (keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); @@ -1713,7 +1735,8 @@ get_public_key (app_t app, int keyno) hexkeyid = fpr + 24; ret = gpgrt_asprintf - (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr); + (&command, "%s --list-keys --with-colons --with-key-data '%s'", + gnupg_module_name (GNUPG_MODULE_NAME_GPG), fpr); if (ret < 0) { err = gpg_error_from_syserror (); @@ -1759,17 +1782,17 @@ get_public_key (app_t app, int keyno) app->app_local->pk[keyno].key = (unsigned char*)keybuf; /* Decrement for trailing '\0' */ app->app_local->pk[keyno].keylen = len - 1; + err = store_keygrip (app, keyno); } leave: /* Set a flag to indicate that we tried to read the key. */ - app->app_local->pk[keyno].read_done = 1; + if (!err) + app->app_local->pk[keyno].read_done = 1; xfree (buffer); return err; } -#endif /* GNUPG_MAJOR_VERSION > 1 */ - /* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3]. @@ -1779,11 +1802,6 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) { int keyno = key - 1; gpg_error_t err = 0; - /* Note that GnuPG 1.x does not need this and it would be too time - consuming to send it just for the fun of it. */ -#if GNUPG_MAJOR_VERSION > 1 - unsigned char grip[20]; - char gripstr[41]; char idbuf[50]; const char *usage; @@ -1795,14 +1813,6 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) if (!app->app_local->pk[keyno].key) goto leave; /* No such key - ignore. */ - err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key, - app->app_local->pk[keyno].keylen, - grip); - if (err) - goto leave; - - bin2hex (grip, 20, gripstr); - switch (keyno) { case 0: usage = "sc"; break; @@ -1813,14 +1823,12 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) sprintf (idbuf, "OPENPGP.%d", keyno+1); send_status_info (ctrl, "KEYPAIRINFO", - gripstr, 40, + app->app_local->pk[keyno].keygrip_str, 40, idbuf, strlen (idbuf), usage, strlen (usage), NULL, (size_t)0); leave: -#endif /* GNUPG_MAJOR_VERSION > 1 */ - return err; } @@ -1875,7 +1883,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) buffer. On error PK and PKLEN are not changed and an error code is returned. */ static gpg_error_t -do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) +do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, + unsigned char **pk, size_t *pklen) { gpg_error_t err; int keyno; @@ -1898,15 +1907,25 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) if (!buf) return gpg_error (GPG_ERR_NO_PUBKEY); - *pklen = app->app_local->pk[keyno].keylen; - *pk = xtrymalloc (*pklen); - if (!*pk) + if ((flags & APP_READKEY_FLAG_INFO)) { - err = gpg_error_from_syserror (); - *pklen = 0; - return err; + err = send_keypair_info (app, ctrl, keyno+1); + if (err) + return err; + } + + if (pk && pklen) + { + *pklen = app->app_local->pk[keyno].keylen; + *pk = xtrymalloc (*pklen); + if (!*pk) + { + err = gpg_error_from_syserror (); + *pklen = 0; + return err; + } + memcpy (*pk, buf, *pklen); } - memcpy (*pk, buf, *pklen); return 0; } @@ -1919,7 +1938,6 @@ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { -#if GNUPG_MAJOR_VERSION > 1 gpg_error_t err; unsigned char *buffer; size_t buflen; @@ -1948,9 +1966,6 @@ do_readcert (app_t app, const char *certid, } xfree (relptr); return err; -#else - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); -#endif } @@ -2150,7 +2165,7 @@ verify_a_chv (app_t app, /* Special case for def_chv2 mechanism. */ if (opt.verbose) log_info (_("using default PIN as %s\n"), "CHV2"); - rc = iso7816_verify (app->slot, 0x82, "123456", 6); + rc = iso7816_verify (app_get_slot (app), 0x82, "123456", 6); if (rc) { /* Verification of CHV2 with the default PIN failed, @@ -2183,7 +2198,7 @@ verify_a_chv (app_t app, } if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) && !check_pinpad_request (app, &pininfo, 0)) { /* The reader supports the verify command through the pinpad. @@ -2199,7 +2214,7 @@ verify_a_chv (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x80+chvno, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); @@ -2230,7 +2245,8 @@ verify_a_chv (app_t app, rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen); if (!rc) - rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen); + rc = iso7816_verify (app_get_slot (app), + 0x80 + chvno, *pinvalue, *pinlen); } if (rc) @@ -2270,7 +2286,7 @@ verify_chv2 (app_t app, the card is not configured to require a verification before each CHV1 controlled operation (force_chv1) and if we are not using the pinpad (PINVALUE == NULL). */ - rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x81, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) @@ -2337,13 +2353,11 @@ verify_chv3 (app_t app, { int rc = 0; -#if GNUPG_MAJOR_VERSION != 1 if (!opt.allow_admin) { log_info (_("access to admin commands is not configured\n")); return gpg_error (GPG_ERR_EACCES); } -#endif if (!app->did_chv3) { @@ -2360,7 +2374,8 @@ verify_chv3 (app_t app, return rc; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) + && !iso7816_check_pinpad (app_get_slot (app), + ISO7816_VERIFY, &pininfo) && !check_pinpad_request (app, &pininfo, 1)) { /* The reader supports the verify command through the pinpad. */ @@ -2373,7 +2388,7 @@ verify_chv3 (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x83, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x83, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } @@ -2402,7 +2417,7 @@ verify_chv3 (app_t app, rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); if (!rc) - rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen); xfree (pinvalue); } @@ -2501,7 +2516,8 @@ do_setattr (app_t app, const char *name, exmode = -254; /* Command chaining with max. 254 bytes. */ else exmode = 0; - rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen); + rc = iso7816_put_data (app_get_slot (app), + exmode, table[idx].tag, value, valuelen); if (rc) log_error ("failed to set '%s': %s\n", table[idx].name, gpg_strerror (rc)); @@ -2532,7 +2548,6 @@ do_writecert (app_t app, ctrl_t ctrl, const unsigned char *certdata, size_t certdatalen) { (void)ctrl; -#if GNUPG_MAJOR_VERSION > 1 if (strcmp (certidstr, "OPENPGP.3")) return gpg_error (GPG_ERR_INV_ID); if (!certdata || !certdatalen) @@ -2542,9 +2557,6 @@ do_writecert (app_t app, ctrl_t ctrl, if (certdatalen > app->app_local->extcap.max_certlen_3) return gpg_error (GPG_ERR_TOO_LARGE); return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen); -#else - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); -#endif } @@ -2562,7 +2574,7 @@ clear_chv_status (app_t app, int chvno) apdu[2] = 0xff; apdu[3] = 0x80+chvno; - err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, NULL, NULL, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_INV_VALUE) @@ -2573,7 +2585,8 @@ clear_chv_status (app_t app, int chvno) if (chvno == 1) { apdu[3]++; - err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), + apdu, 4, 0, NULL, NULL, NULL); app->did_chv1 = app->did_chv2 = 0; } else if (chvno == 2) @@ -2684,7 +2697,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, /* Version 2 cards. */ if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_CHANGE_REFERENCE_DATA, &pininfo) && !check_pinpad_request (app, &pininfo, chvno == 3)) use_pinpad = 1; @@ -2827,7 +2840,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen); } if (!rc) - rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, + rc = iso7816_reset_retry_counter_with_rc (app_get_slot (app), 0x81, buffer, pinlen0+pinlen); wipememory (buffer, pinlen0 + pinlen); xfree (buffer); @@ -2844,31 +2857,37 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, { rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen); if (!rc) - rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen); + rc = iso7816_put_data (app_get_slot (app), + 0, 0xD3, pinvalue, pinlen); } } else if (reset_mode) { rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen); if (!rc) - rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen); + rc = iso7816_reset_retry_counter (app_get_slot (app), + 0x81, pinvalue, pinlen); if (!rc && !app->app_local->extcap.is_v2) - rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen); + rc = iso7816_reset_retry_counter (app_get_slot (app), + 0x82, pinvalue, pinlen); } else if (!app->app_local->extcap.is_v2) { /* Version 1 cards. */ if (chvno == 1 || chvno == 2) { - rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x81, NULL, 0, pinvalue, strlen (pinvalue)); if (!rc) - rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x82, NULL, 0, pinvalue, strlen (pinvalue)); } else /* CHVNO == 3 */ { - rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x80 + chvno, NULL, 0, pinvalue, strlen (pinvalue)); } } @@ -2889,7 +2908,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, gpg_strerror (rc)); goto leave; } - rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0, + rc = iso7816_change_reference_data_kp (app_get_slot (app), + 0x80 + chvno, 0, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } @@ -2899,7 +2919,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, if (!rc) rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen); if (!rc) - rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x80 + chvno, oldpinvalue, pinlen0, pinvalue, pinlen); } @@ -2942,7 +2963,7 @@ does_key_exist (app_t app, int keyidx, int generating, int force) assert (keyidx >=0 && keyidx <= 2); - if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen)) + if (iso7816_get_data (app_get_slot (app), 0, 0x006E, &buffer, &buflen)) { log_error (_("error reading application data\n")); return gpg_error (GPG_ERR_GENERAL); @@ -3256,7 +3277,7 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen, return err; /* Change the attribute. */ - err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buf, buflen); + err = iso7816_put_data (app_get_slot (app), 0, 0xC1+keyno, buf, buflen); if (err) log_error ("error changing key attribute (key=%d)\n", keyno+1); else @@ -3648,7 +3669,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), exmode = -254; else exmode = 0; - err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, + err = iso7816_put_data_odd (app_get_slot (app), exmode, 0x3fff, template, template_len); } else @@ -3698,7 +3719,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; /* Store the key. */ - err = iso7816_put_data (app->slot, 0, + err = iso7816_put_data (app_get_slot (app), 0, (app->appversion > 0x0007? 0xE0:0xE9)+keyno, template, template_len); } @@ -3970,7 +3991,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), exmode = -254; else exmode = 0; - err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, + err = iso7816_put_data_odd (app_get_slot (app), exmode, 0x3fff, template, template_len); xfree (template); } @@ -4138,7 +4159,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); - err = iso7816_generate_keypair (app->slot, exmode, 0x80, 0, + err = iso7816_generate_keypair (app_get_slot (app), exmode, 0x80, 0, (keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); @@ -4269,6 +4290,74 @@ check_against_given_fingerprint (app_t app, const char *fpr, int key) } +/* Check KEYIDSTR, if it's valid. + When KEYNO is 0, it means it's for PIN check. + Otherwise, KEYNO corresponds to the slot (signing, decipher and auth). + KEYIDSTR is either: + (1) Serial number + (2) Serial number "/" fingerprint + (3) keygrip + + When KEYNO is 0 and KEYIDSTR is for a keygrip, the keygrip should + be to be compared is the first one (keygrip for signing). + */ +static int +check_keyidstr (app_t app, const char *keyidstr, int keyno) +{ + int rc; + const char *s; + int n; + const char *fpr = NULL; + unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */ + + if (strlen (keyidstr) < 32) + return gpg_error (GPG_ERR_INV_ID); + else + { + for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + ; + + /* Check if it's a keygrip */ + if (n == 40) + { + const unsigned char *keygrip_str; + + keygrip_str = app->app_local->pk[keyno?keyno-1:0].keygrip_str; + if (!strncmp (keygrip_str, keyidstr, 40)) + return 0; + else + return gpg_error (GPG_ERR_INV_ID); + } + + if (n != 32 || strncmp (keyidstr, "D27600012401", 12)) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* no fingerprint given: we allow this for now. */ + else if (*s == '/') + fpr = s + 1; + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; n < 16; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + + if (app->card->serialnolen != 16) + return gpg_error (GPG_ERR_INV_CARD); + if (memcmp (app->card->serialno, tmp_sn, 16)) + return gpg_error (GPG_ERR_WRONG_CARD); + } + + /* If a fingerprint has been specified check it against the one on + the card. This is allows for a meaningful error message in case + the key on the card has been replaced but the shadow information + known to gpg was not updated. If there is no fingerprint, gpg + will detect a bogus signature anyway due to the + verify-after-signing feature. */ + rc = (fpr&&keyno)? check_against_given_fingerprint (app, fpr, keyno) : 0; + + return rc; +} + /* Compute a digital signature on INDATA which is expected to be the raw message digest. For this application the KEYIDSTR consists of @@ -4314,10 +4403,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, int rc; unsigned char data[19+64]; size_t datalen; - unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */ - const char *s; - int n; - const char *fpr = NULL; unsigned long sigcount; int use_auth = 0; int exmode, le_value; @@ -4362,40 +4447,13 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, ; else if (!strcmp (keyidstr, "OPENPGP.3")) use_auth = 1; - else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); else { - for (s=keyidstr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 32) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* no fingerprint given: we allow this for now. */ - else if (*s == '/') - fpr = s + 1; - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=keyidstr, n=0; n < 16; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - - if (app->serialnolen != 16) - return gpg_error (GPG_ERR_INV_CARD); - if (memcmp (app->serialno, tmp_sn, 16)) - return gpg_error (GPG_ERR_WRONG_CARD); + rc = check_keyidstr (app, keyidstr, 1); + if (rc) + return rc; } - /* If a fingerprint has been specified check it against the one on - the card. This is allows for a meaningful error message in case - the key on the card has been replaced but the shadow information - known to gpg was not updated. If there is no fingerprint, gpg - will detect a bogus signature anyway due to the - verify-after-signing feature. */ - rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0; - if (rc) - return rc; - /* Concatenate prefix and digest. */ #define X(a,b,d) \ if (hashalgo == GCRY_MD_ ## a && (d) ) \ @@ -4443,7 +4501,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, char *pinvalue; int pinlen; - rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen); + rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, + &pinvalue, &pinlen); if (rc) return rc; @@ -4456,7 +4515,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, pinpad has been used. */ if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2) { - rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x82, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) @@ -4484,7 +4543,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, exmode = 0; le_value = 0; } - rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value, + rc = iso7816_compute_ds (app_get_slot (app), exmode, data, datalen, le_value, outdata, outdatalen); if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) clear_chv_status (app, 1); @@ -4512,10 +4571,6 @@ do_auth (app_t app, const char *keyidstr, unsigned char **outdata, size_t *outdatalen ) { int rc; - unsigned char tmp_sn[20]; /* Actually 16 but we use it also for the fpr. */ - const char *s; - int n; - const char *fpr = NULL; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); @@ -4543,40 +4598,13 @@ do_auth (app_t app, const char *keyidstr, /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.3")) ; - else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); else { - for (s=keyidstr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 32) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* no fingerprint given: we allow this for now. */ - else if (*s == '/') - fpr = s + 1; - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=keyidstr, n=0; n < 16; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - - if (app->serialnolen != 16) - return gpg_error (GPG_ERR_INV_CARD); - if (memcmp (app->serialno, tmp_sn, 16)) - return gpg_error (GPG_ERR_WRONG_CARD); + rc = check_keyidstr (app, keyidstr, 3); + if (rc) + return rc; } - /* If a fingerprint has been specified check it against the one on - the card. This is allows for a meaningful error message in case - the key on the card has been replaced but the shadow information - known to gpg was not updated. If there is no fingerprint, gpg - will detect a bogus signature anyway due to the - verify-after-signing feature. */ - rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0; - if (rc) - return rc; - rc = verify_chv2 (app, pincb, pincb_arg); if (!rc) { @@ -4594,7 +4622,7 @@ do_auth (app_t app, const char *keyidstr, exmode = 0; le_value = 0; } - rc = iso7816_internal_authenticate (app->slot, exmode, + rc = iso7816_internal_authenticate (app_get_slot (app), exmode, indata, indatalen, le_value, outdata, outdatalen); if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) @@ -4612,11 +4640,8 @@ do_decipher (app_t app, const char *keyidstr, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info) { - int rc; - unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */ - const char *s; int n; - const char *fpr = NULL; + int rc; int exmode, le_value; unsigned char *fixbuf = NULL; int padind = 0; @@ -4628,39 +4653,13 @@ do_decipher (app_t app, const char *keyidstr, /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.2")) ; - else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); else { - for (s=keyidstr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 32) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* no fingerprint given: we allow this for now. */ - else if (*s == '/') - fpr = s + 1; - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=keyidstr, n=0; n < 16; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - - if (app->serialnolen != 16) - return gpg_error (GPG_ERR_INV_CARD); - if (memcmp (app->serialno, tmp_sn, 16)) - return gpg_error (GPG_ERR_WRONG_CARD); + rc = check_keyidstr (app, keyidstr, 2); + if (rc) + return rc; } - /* If a fingerprint has been specified check it against the one on - the card. This is allows for a meaningful error message in case - the key on the card has been replaced but the shadow information - known to gpg was not updated. If there is no fingerprint, the - decryption won't produce the right plaintext anyway. */ - rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0; - if (rc) - return rc; - rc = verify_chv2 (app, pincb, pincb_arg); if (rc) return rc; @@ -4818,7 +4817,7 @@ do_decipher (app_t app, const char *keyidstr, else exmode = le_value = 0; - rc = iso7816_decipher (app->slot, exmode, + rc = iso7816_decipher (app_get_slot (app), exmode, indata, indatalen, le_value, padind, outdata, outdatalen); xfree (fixbuf); @@ -4876,38 +4875,19 @@ do_check_pin (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { - unsigned char tmp_sn[20]; - const char *s; - int n; int admin_pin = 0; + int rc; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); - /* Check whether an OpenPGP card of any version has been requested. */ - if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); + rc = check_keyidstr (app, keyidstr, 0); + if (rc) + return rc; - for (s=keyidstr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 32) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* No fingerprint given: we allow this for now. */ - else if (*s == '/') - ; /* We ignore a fingerprint. */ - else if (!strcmp (s, "[CHV3]") ) + if ((strlen (keyidstr) >= 32+6 && !strcmp (keyidstr+32, "[CHV3]")) + || (strlen (keyidstr) >= 40+6 && !strcmp (keyidstr+40, "[CHV3]"))) admin_pin = 1; - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=keyidstr, n=0; n < 16; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - - if (app->serialnolen != 16) - return gpg_error (GPG_ERR_INV_CARD); - if (memcmp (app->serialno, tmp_sn, 16)) - return gpg_error (GPG_ERR_WRONG_CARD); /* Yes, there is a race conditions: The user might pull the card right here and we won't notice that. However this is not a @@ -4950,6 +4930,65 @@ do_check_pin (app_t app, const char *keyidstr, return verify_chv2 (app, pincb, pincb_arg); } +static gpg_error_t +do_with_keygrip (app_t app, ctrl_t ctrl, int action, const char *keygrip_str) +{ + int i; + + /* Make sure we have load the public keys. */ + for (i = 0; i < 3; i++) + get_public_key (app, i); + + if (action == KEYGRIP_ACTION_LOOKUP) + { + if (keygrip_str == NULL) + return gpg_error (GPG_ERR_NOT_FOUND); + + for (i = 0; i < 3; i++) + if (app->app_local->pk[i].read_done + && !strcmp (keygrip_str, app->app_local->pk[i].keygrip_str)) + return 0; /* Found */ + } + else + { + char idbuf[50]; + char buf[65]; + int data = (action == KEYGRIP_ACTION_SEND_DATA); + + if (DIM (buf) < 2 * app->card->serialnolen + 1) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + + bin2hex (app->card->serialno, app->card->serialnolen, buf); + + if (keygrip_str == NULL) + { + for (i = 0; i < 3; i++) + if (app->app_local->pk[i].read_done) + { + sprintf (idbuf, "OPENPGP.%d", i+1); + send_keyinfo (ctrl, data, + app->app_local->pk[i].keygrip_str,buf, idbuf); + } + /* Return an error so that the dispatcher keeps on looping + * over the other applications. Only for clarity we use a + * different error code than for the not_found case. */ + return gpg_error (GPG_ERR_TRUE); + } + else + { + for (i = 0; i < 3; i++) + if (app->app_local->pk[i].read_done + && !strcmp (keygrip_str, app->app_local->pk[i].keygrip_str)) + { + sprintf (idbuf, "OPENPGP.%d", i+1); + send_keyinfo (ctrl, data, keygrip_str, buf, idbuf); + return 0; + } + } + } + + return gpg_error (GPG_ERR_NOT_FOUND); +} /* Show information about card capabilities. */ static void @@ -5170,13 +5209,36 @@ parse_algorithm_attribute (app_t app, int keyno) xfree (relptr); } + +/* Reselect the application. This is used by cards which support + * on-the-fly switching between applications. */ +static gpg_error_t +do_reselect (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + + (void)ctrl; + + /* An extra check which should not be necessary because the caller + * should have made sure that a re-select is only called for + * approriate cards. */ + if (app->card->cardtype != CARDTYPE_YUBIKEY) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + /* Note that the card can't cope with P2=0xCO, thus we need to pass + * a special flag value. */ + err = iso7816_select_application (app_get_slot (app), + openpgp_aid, sizeof openpgp_aid, 0x0001); + return err; +} + + /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ gpg_error_t app_select_openpgp (app_t app) { - static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; - int slot = app->slot; + int slot = app_get_slot (app); int rc; unsigned char *buffer; size_t buflen; @@ -5184,12 +5246,13 @@ app_select_openpgp (app_t app) /* Note that the card can't cope with P2=0xCO, thus we need to pass a special flag value. */ - rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001); + rc = iso7816_select_application (slot, + openpgp_aid, sizeof openpgp_aid, 0x0001); if (!rc) { unsigned int manufacturer; - app->apptype = "OPENPGP"; + app->apptype = APPTYPE_OPENPGP; app->did_chv1 = 0; app->did_chv2 = 0; @@ -5214,9 +5277,9 @@ app_select_openpgp (app_t app) app->appversion |= buffer[7]; manufacturer = (buffer[8]<<8 | buffer[9]); - xfree (app->serialno); - app->serialno = buffer; - app->serialnolen = buflen; + xfree (app->card->serialno); + app->card->serialno = buffer; + app->card->serialnolen = buflen; buffer = NULL; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -5319,6 +5382,7 @@ app_select_openpgp (app_t app) dump_all_do (slot); app->fnc.deinit = do_deinit; + app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; @@ -5332,6 +5396,7 @@ app_select_openpgp (app_t app) app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_pin; app->fnc.check_pin = do_check_pin; + app->fnc.with_keygrip = do_with_keygrip; } leave: diff --git a/scd/app-p15.c b/scd/app-p15.c index 190292433..ce82b34a9 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -38,7 +38,6 @@ #include "scdaemon.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "apdu.h" /* fixme: we should move the card detection to a separate file */ @@ -443,7 +442,7 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) if (app->app_local->direct_path_selection) { - err = iso7816_select_path (app->slot, path+1, pathlen-1); + err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1); if (err) { log_error ("error selecting path "); @@ -461,7 +460,8 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) supported by the card. */ for (i=0; i < pathlen; i++) { - err = iso7816_select_file (app->slot, path[i], !(i+1 == pathlen)); + err = iso7816_select_file (app_get_slot (app), + path[i], !(i+1 == pathlen)); if (err) { log_error ("error selecting part %d from path ", i); @@ -612,7 +612,8 @@ read_ef_odf (app_t app, unsigned short odf_fid) unsigned short value; size_t offset; - err = select_and_read_binary (app->slot, odf_fid, "ODF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF", + &buffer, &buflen); if (err) return err; @@ -826,7 +827,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ - err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), fid, "PrKDF", + &buffer, &buflen); if (err) return err; @@ -1282,7 +1284,8 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ - err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), fid, "CDF", + &buffer, &buflen); if (err) return err; @@ -1555,7 +1558,8 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */ - err = select_and_read_binary (app->slot, fid, "AODF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), fid, "AODF", + &buffer, &buflen); if (err) return err; @@ -2216,7 +2220,7 @@ read_ef_tokeninfo (app_t app) int class, tag, constructed, ndef; unsigned long ul; - err = select_and_read_binary (app->slot, 0x5032, "TokenInfo", + err = select_and_read_binary (app_get_slot (app), 0x5032, "TokenInfo", &buffer, &buflen); if (err) return err; @@ -2294,13 +2298,13 @@ read_p15_info (app_t app) { /* If we don't have a serial number yet but the TokenInfo provides one, use that. */ - if (!app->serialno && app->app_local->serialno) + if (!app->card->serialno && app->app_local->serialno) { - app->serialno = app->app_local->serialno; - app->serialnolen = app->app_local->serialnolen; + app->card->serialno = app->app_local->serialno; + app->card->serialnolen = app->app_local->serialnolen; app->app_local->serialno = NULL; app->app_local->serialnolen = 0; - err = app_munge_serialno (app); + err = app_munge_serialno (app->card); if (err) return err; } @@ -2553,7 +2557,8 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, if (err) goto leave; - err = iso7816_read_binary (app->slot, cdf->off, cdf->len, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), cdf->off, cdf->len, + &buffer, &buflen); if (!err && (!buflen || *buffer == 0xff)) err = gpg_error (GPG_ERR_NOT_FOUND); if (err) @@ -2713,7 +2718,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) err = select_ef_by_path (app, path, DIM(path) ); if (!err) - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, + &buffer, &buflen); if (err) { log_error ("error accessing EF(ID): %s\n", gpg_strerror (err)); @@ -2758,7 +2764,7 @@ micardo_mse (app_t app, unsigned short fid) unsigned char msebuf[10]; /* Read the KeyD file containing extra information on keys. */ - err = iso7816_select_file (app->slot, 0x0013, 0); + err = iso7816_select_file (app_get_slot (app), 0x0013, 0); if (err) { log_error ("error reading EF_keyD: %s\n", gpg_strerror (err)); @@ -2772,7 +2778,8 @@ micardo_mse (app_t app, unsigned short fid) size_t n, nn; const unsigned char *p, *pp; - err = iso7816_read_record (app->slot, recno, 1, 0, &buffer, &buflen); + err = iso7816_read_record (app_get_slot (app), recno, 1, 0, + &buffer, &buflen); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) break; /* ready */ if (err) @@ -2811,7 +2818,8 @@ micardo_mse (app_t app, unsigned short fid) /* Restore the security environment to SE_NUM if needed */ if (se_num) { - err = iso7816_manage_security_env (app->slot, 0xf3, se_num, NULL, 0); + err = iso7816_manage_security_env (app_get_slot (app), + 0xf3, se_num, NULL, 0); if (err) { log_error ("restoring SE to %d failed: %s\n", @@ -2826,7 +2834,7 @@ micardo_mse (app_t app, unsigned short fid) msebuf[2] = 0x80; msebuf[3] = (refdata >> 8); msebuf[4] = refdata; - err = iso7816_manage_security_env (app->slot, 0x41, 0xb6, msebuf, 5); + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xb6, msebuf, 5); if (err) { log_error ("setting SE to reference file %04hX failed: %s\n", @@ -2948,7 +2956,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, mse[3] = 0x84; /* Private key reference tag. */ mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82; - err = iso7816_manage_security_env (app->slot, + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); no_data_padding = 1; @@ -3101,7 +3109,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, else pinvaluelen = strlen (pinvalue); - err = iso7816_verify (app->slot, + err = iso7816_verify (app_get_slot (app), aodf->pin_reference_valid? aodf->pin_reference : 0, pinvalue, pinvaluelen); xfree (pinvalue); @@ -3170,7 +3178,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, mse[1] = 1; mse[2] = prkdf->key_reference; - err = iso7816_manage_security_env (app->slot, + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } @@ -3181,11 +3189,14 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, } if (hashalgo == MD_USER_TLS_MD5SHA1) - err = iso7816_compute_ds (app->slot, 0, data, 36, 0, outdata, outdatalen); + err = iso7816_compute_ds (app_get_slot (app), + 0, data, 36, 0, outdata, outdatalen); else if (no_data_padding) - err = iso7816_compute_ds (app->slot, 0, data+15, 20, 0,outdata,outdatalen); + err = iso7816_compute_ds (app_get_slot (app), + 0, data+15, 20, 0,outdata,outdatalen); else - err = iso7816_compute_ds (app->slot, 0, data, 35, 0, outdata, outdatalen); + err = iso7816_compute_ds (app_get_slot (app), + 0, data, 35, 0, outdata, outdatalen); return err; } @@ -3280,7 +3291,7 @@ read_home_df (int slot, int *r_belpic) gpg_error_t app_select_p15 (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; unsigned short def_home_df = 0; card_type_t card_type = CARD_TYPE_UNKNOWN; @@ -3298,7 +3309,7 @@ app_select_p15 (app_t app) Using the 2f02 just works. */ unsigned short path[1] = { 0x2f00 }; - rc = iso7816_select_path (app->slot, path, 1); + rc = iso7816_select_path (app_get_slot (app), path, 1); if (!rc) { direct = 1; @@ -3306,7 +3317,7 @@ app_select_p15 (app_t app) if (def_home_df) { path[0] = def_home_df; - rc = iso7816_select_path (app->slot, path, 1); + rc = iso7816_select_path (app_get_slot (app), path, 1); } } } @@ -3330,7 +3341,7 @@ app_select_p15 (app_t app) size_t atrlen; int i; - atr = apdu_get_atr (app->slot, &atrlen); + atr = apdu_get_atr (app_get_slot (app), &atrlen); if (!atr) rc = gpg_error (GPG_ERR_INV_CARD); else @@ -3348,7 +3359,7 @@ app_select_p15 (app_t app) } if (!rc) { - app->apptype = "P15"; + app->apptype = APPTYPE_P15; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -3381,8 +3392,8 @@ app_select_p15 (app_t app) prototype card right here because we need to access to EF(TokenInfo). We mark such a serial number by the using a prefix of FF0100. */ - if (app->serialnolen == 12 - && !memcmp (app->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) + if (app->card->serialnolen == 12 + && !memcmp (app->card->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) { /* This is a German card with a silly serial number. Try to get the serial number from the EF(TokenInfo). . */ @@ -3390,20 +3401,21 @@ app_select_p15 (app_t app) /* FIXME: actually get it from EF(TokenInfo). */ - p = xtrymalloc (3 + app->serialnolen); + p = xtrymalloc (3 + app->card->serialnolen); if (!p) rc = gpg_error (gpg_err_code_from_errno (errno)); else { memcpy (p, "\xff\x01", 3); - memcpy (p+3, app->serialno, app->serialnolen); - app->serialnolen += 3; - xfree (app->serialno); - app->serialno = p; + memcpy (p+3, app->card->serialno, app->card->serialnolen); + app->card->serialnolen += 3; + xfree (app->card->serialno); + app->card->serialno = p; } } app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = do_getattr; diff --git a/scd/app-piv.c b/scd/app-piv.c index 5748c70b8..3b94a28e4 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -19,6 +19,7 @@ /* Some notes: * - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4 + * - https://developers.yubico.com/PIV/Introduction/PIV_attestation.html * * - Access control matrix: * | Action | 9B | PIN | PUK | | @@ -60,7 +61,6 @@ #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "../common/host2net.h" #include "apdu.h" /* We use apdu_send_direct. */ @@ -81,6 +81,10 @@ #define PIV_ALGORITHM_ECC_P384 0x14 +/* The AID for PIV. */ +static char const piv_aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */ + 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ }; + /* A table describing the DOs of a PIV card. */ struct data_object_s @@ -282,7 +286,7 @@ get_cached_data (app_t app, int tag, } } - err = iso7816_get_data_odd (app->slot, 0, tag, &p, &len); + err = iso7816_get_data_odd (app_get_slot (app), 0, tag, &p, &len); if (err) return err; @@ -736,18 +740,18 @@ get_dispserialno (app_t app, int failmode) { char *result; - if (app->serialno && app->serialnolen == 3+1+4 - && !memcmp (app->serialno, "\xff\x02\x00", 3)) + if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4 + && !memcmp (app->card->serialno, "\xff\x02\x00", 3)) { /* This is a 4 byte S/N of a Yubikey which seems to be printed * on the token in decimal. Maybe they will print larger S/N * also in decimal but we can't be sure, thus do it only for * these 32 bit numbers. */ unsigned long sn; - sn = app->serialno[4] * 16777216; - sn += app->serialno[5] * 65536; - sn += app->serialno[6] * 256; - sn += app->serialno[7]; + sn = app->card->serialno[4] * 16777216; + sn += app->card->serialno[5] * 65536; + sn += app->card->serialno[6] * 256; + sn += app->card->serialno[7]; result = xtryasprintf ("yk-%lu", sn); } else if (failmode) @@ -785,7 +789,7 @@ get_chv_status (app_t app, const char *keyrefstr) apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; - if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL)) + if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) result = -5; /* No need to verification. */ else if (sw == 0x6a88 || sw == 0x6a80) result = -2; /* No such PIN. */ @@ -811,7 +815,9 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) int special; } table[] = { { "SERIALNO", 0x0000, -1 }, - { "$AUTHKEYID", 0x0000, -2 }, /* Default key for ssh. */ + { "$AUTHKEYID", 0x0000, -2 }, /* Default ssh key. */ + { "$ENCRKEYID", 0x0000, -6 }, /* Default encryption key. */ + { "$SIGNKEYID", 0x0000, -7 }, /* Default signing key. */ { "$DISPSERIALNO",0x0000, -3 }, { "CHV-STATUS", 0x0000, -4 }, { "CHV-USAGE", 0x007E, -5 } @@ -882,6 +888,16 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) xfree (relptr); } } + else if (table[idx].special == -6) + { + char const tmp[] = "PIV.9D"; /* Key Management. */ + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + } + else if (table[idx].special == -7) + { + char const tmp[] = "PIV.9C"; /* Digital Signature. */ + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + } else { relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err); @@ -925,7 +941,7 @@ auth_adm_key (app_t app, const unsigned char *value, size_t valuelen) tmpl[2] = 0x80; tmpl[3] = 0; /* (Empty witness requests a witness.) */ tmpllen = 4; - err = iso7816_general_authenticate (app->slot, 0, + err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); @@ -958,7 +974,7 @@ auth_adm_key (app_t app, const unsigned char *value, size_t valuelen) tmpl[23] = 0; tmpllen = 24; xfree (outdata); - err = iso7816_general_authenticate (app->slot, 0, + err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); @@ -1032,7 +1048,8 @@ set_adm_key (app_t app, const unsigned char *value, size_t valuelen) apdu[6] = 0x9b; apdu[7] = 24; memcpy (apdu+8, value, 24); - err = iso7816_apdu_direct (app->slot, apdu, 8+24, 0, &sw, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), apdu, 8+24, 0, + &sw, NULL, NULL); wipememory (apdu+8, 24); if (err) log_error ("piv: setting admin key failed; sw=%04x\n", sw); @@ -1413,7 +1430,7 @@ do_readcert (app_t app, const char *certid, apdu[1] = 0xf9; /* Yubikey: Get attestation cert. */ apdu[2] = xtoi_2 (certid+9); apdu[3] = 0; - err = iso7816_apdu_direct (app->slot, apdu, 4, 1, + err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 1, NULL, &result, &resultlen); if (!err) { @@ -1452,7 +1469,7 @@ do_readcert (app_t app, const char *certid, * returned. */ static gpg_error_t -do_readkey (app_t app, const char *keyrefstr, +do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags, unsigned char **r_pk, size_t *r_pklen) { gpg_error_t err; @@ -1504,9 +1521,35 @@ do_readkey (app_t app, const char *keyrefstr, goto leave; } - *r_pk = pk; - pk = NULL; - *r_pklen = pklen; + if ((flags & APP_READKEY_FLAG_INFO)) + { + char keygripstr[KEYGRIP_LEN*2+1]; + char idbuf[50]; + const char *usage; + + err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr); + if (err) + { + log_error ("app_help_get_keygrip_string_pk failed: %s\n", + gpg_strerror (err)); + goto leave; + } + usage = dobj->usage? dobj->usage : ""; + + snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref); + send_status_info (ctrl, "KEYPAIRINFO", + keygripstr, strlen (keygripstr), + idbuf, strlen (idbuf), + usage, strlen (usage), + NULL, (size_t)0); + } + + if (r_pk && r_pklen) + { + *r_pk = pk; + pk = NULL; + *r_pklen = pklen; + } leave: gcry_sexp_release (s_pkey); @@ -1841,7 +1884,7 @@ verify_chv (app_t app, int keyref, int force, apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; - if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL)) + if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) { if (!force) /* No need to verification. */ return 0; /* All fine. */ @@ -1857,7 +1900,7 @@ verify_chv (app_t app, int keyref, int force, if (err) return err; - err = iso7816_verify (app->slot, keyref, pin, pinlen); + err = iso7816_verify (app_get_slot (app), keyref, pin, pinlen); wipememory (pin, pinlen); xfree (pin); if (err) @@ -1918,7 +1961,8 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, apdu[1] = ISO7816_VERIFY; apdu[2] = 0xff; apdu[3] = keyref; - err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, + NULL, NULL, NULL); goto leave; } @@ -1940,7 +1984,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; - if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL)) + if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) remaining = -1; /* Already verified, thus full number of tries. */ else if ((sw & 0xfff0) == 0x63C0) remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */ @@ -1957,7 +2001,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, * old is wrong. This is not possible for the PUK, though. */ if (keyref != 0x81) { - err = iso7816_verify (app->slot, keyref, oldpin, oldpinlen); + err = iso7816_verify (app_get_slot (app), keyref, oldpin, oldpinlen); if (err) { log_error ("CHV %02X verification failed: %s\n", @@ -1982,7 +2026,8 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, } memcpy (buf, oldpin, oldpinlen); memcpy (buf+oldpinlen, newpin, newpinlen); - err = iso7816_reset_retry_counter_with_rc (app->slot, targetkeyref, + err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), + targetkeyref, buf, oldpinlen+newpinlen); xfree (buf); if (err) @@ -1991,7 +2036,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, } else { - err = iso7816_change_reference_data (app->slot, keyref, + err = iso7816_change_reference_data (app_get_slot (app), keyref, oldpin, oldpinlen, newpin, newpinlen); if (err) @@ -2230,7 +2275,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, goto leave; /* Note: the -1 requests command chaining. */ - err = iso7816_general_authenticate (app->slot, -1, + err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); @@ -2395,15 +2440,34 @@ do_decipher (app_t app, const char *keyidstr, /* Check that the ciphertext has the right length; due to internal * convey mechanism using MPIs leading zero bytes might have been - * lost. Adjust for this. Note that for ECC this actually - * superfluous because the first octet is always '04' to indicate an + * lost. Adjust for this. Unfortunately the ciphertext might have + * also been prefixed with a leading zero to make it a positive + * number; that may be a too long frame and we need to adjust for + * this too. Note that for ECC thoses fixes are not reqquired + * because the first octet is always '04' to indicate an * uncompressed point. */ if (indatalen > framelen) { - err = gpg_error (GPG_ERR_INV_VALUE); - log_error ("piv: input of %zu octets too large for mechanism %d\n", - indatalen, mechanism); - goto leave; + if (mechanism == PIV_ALGORITHM_RSA + && indatalen == framelen + 1 && !*indata) + { + indata_buffer = xtrycalloc (1, framelen); + if (!indata_buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (indata_buffer, indata+1, framelen); + indata = indata_buffer; + indatalen = framelen; + } + else + { + err = gpg_error (GPG_ERR_INV_VALUE); + log_error ("piv: input of %zu octets too large for mechanism %d\n", + indatalen, mechanism); + goto leave; + } } if (indatalen < framelen) { @@ -2434,7 +2498,7 @@ do_decipher (app_t app, const char *keyidstr, goto leave; /* Note: the -1 requests command chaining. */ - err = iso7816_general_authenticate (app->slot, -1, + err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); @@ -2635,7 +2699,7 @@ writekey_rsa (app_t app, data_object_t dobj, int keyref, if (err) goto leave; - err = iso7816_send_apdu (app->slot, + err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ @@ -2657,7 +2721,7 @@ writekey_rsa (app_t app, data_object_t dobj, int keyref, if (err) goto leave; tmpl[0] = PIV_ALGORITHM_RSA; - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); @@ -2787,7 +2851,7 @@ writekey_ecc (app_t app, data_object_t dobj, int keyref, if (err) goto leave; - err = iso7816_send_apdu (app->slot, + err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ @@ -2808,7 +2872,7 @@ writekey_ecc (app_t app, data_object_t dobj, int keyref, if (err) goto leave; tmpl[0] = mechanism; - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); @@ -2894,7 +2958,7 @@ do_writekey (app_t app, ctrl_t ctrl, /* First clear an existing key. We do this by writing an empty 7f49 * tag. This will return GPG_ERR_NO_PUBKEY on a later read. */ flush_cached_data (app, dobj->tag); - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x7f49, (size_t)0, "", (int)0, (size_t)0, NULL); if (err) @@ -3122,7 +3186,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype, tmpl[3] = 1; tmpl[4] = mechanism; tmpllen = 5; - err = iso7816_generate_keypair (app->slot, 0, 0, keyref, + err = iso7816_generate_keypair (app_get_slot (app), 0, 0, keyref, tmpl, tmpllen, 0, &buffer, &buflen); if (err) { @@ -3152,7 +3216,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype, tmpl[0] = mechanism; flush_cached_data (app, dobj->tag); - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)keydatalen, keydata, (int)0, (size_t)0, NULL); @@ -3205,7 +3269,7 @@ do_writecert (app_t app, ctrl_t ctrl, * GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only * way to detect whether a key exists is by trying to use that * key. */ - err = do_readkey (app, certrefstr, &orig_pk, &orig_pklen); + err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen); if (err) { if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) @@ -3223,7 +3287,7 @@ do_writecert (app_t app, ctrl_t ctrl, goto leave; } - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x70, (size_t)certlen, cert,/* Certificate */ (int)0x71, (size_t)1, "", /* No compress */ (int)0xfe, (size_t)0, "", /* Empty LRC. */ @@ -3242,14 +3306,121 @@ do_writecert (app_t app, ctrl_t ctrl, } +/* Process the various keygrip based info requests. */ +static gpg_error_t +do_with_keygrip (app_t app, ctrl_t ctrl, int action, + const char *want_keygripstr) +{ + gpg_error_t err; + char *keygripstr = NULL; + char *serialno = NULL; + char idbuf[20]; + int data = 0; + int i, tag, dummy_got_cert; + + /* First a quick check for valid parameters. */ + switch (action) + { + case KEYGRIP_ACTION_LOOKUP: + if (!want_keygripstr) + { + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + break; + case KEYGRIP_ACTION_SEND_DATA: + data = 1; + break; + case KEYGRIP_ACTION_WRITE_STATUS: + break; + default: + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + /* Allocate the s/n string if needed. */ + if (action != KEYGRIP_ACTION_LOOKUP) + { + serialno = app_get_serialno (app); + if (!serialno) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + for (i = 0; (tag = data_objects[i].tag); i++) + { + if (!data_objects[i].keypair) + continue; + + xfree (keygripstr); + if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert)) + continue; + + if (action == KEYGRIP_ACTION_LOOKUP) + { + if (!strcmp (keygripstr, want_keygripstr)) + { + err = 0; /* Found */ + goto leave; + } + } + else if (!want_keygripstr || !strcmp (keygripstr, want_keygripstr)) + { + snprintf (idbuf, sizeof idbuf, "PIV.%s", data_objects[i].keyref); + send_keyinfo (ctrl, data, keygripstr, serialno, idbuf); + if (want_keygripstr) + { + err = 0; /* Found */ + goto leave; + } + } + } + + /* Return an error so that the dispatcher keeps on looping over the + * other applications. For clarity we use a different error code + * when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR + * is not NULL. */ + if (!want_keygripstr) + err = gpg_error (GPG_ERR_TRUE); + else + err = gpg_error (GPG_ERR_NOT_FOUND); + + leave: + xfree (keygripstr); + xfree (serialno); + return err; +} + + +/* Reselect the application. This is used by cards which support + * on-the-fly switching between applications. */ +static gpg_error_t +do_reselect (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + + (void)ctrl; + + /* An extra check which should not be necessary because the caller + * should have made sure that a re-select is only called for + * approriate cards. */ + if (!app->app_local->flags.yubikey) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + err = iso7816_select_application (app_get_slot (app), + piv_aid, sizeof piv_aid, 0x0001); + return err; +} + + /* Select the PIV application on the card in SLOT. This function must * be used before any other PIV application functions. */ gpg_error_t app_select_piv (app_t app) { - static char const aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */ - 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ }; - int slot = app->slot; + int slot = app_get_slot (app); gpg_error_t err; unsigned char *apt = NULL; size_t aptlen; @@ -3259,12 +3430,12 @@ app_select_piv (app_t app) /* Note that we select using the AID without the 2 octet version * number. This allows for better reporting of future specs. We * need to use the use-zero-for-P2-flag. */ - err = iso7816_select_application_ext (slot, aid, sizeof aid, 0x0001, + err = iso7816_select_application_ext (slot, piv_aid, sizeof piv_aid, 0x0001, &apt, &aptlen); if (err) goto leave; - app->apptype = "PIV"; + app->apptype = APPTYPE_PIV; app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; @@ -3279,7 +3450,7 @@ app_select_piv (app_t app) } s = find_tlv (apt, aptlen, 0x4F, &n); - if (!s || n != 6 || memcmp (s, aid+5, 4)) + if (!s || n != 6 || memcmp (s, piv_aid+5, 4)) { /* The PIX does not match. */ log_error ("piv: missing or invalid DO 0x4F in APT\n"); @@ -3302,7 +3473,7 @@ app_select_piv (app_t app) goto leave; } s = find_tlv (s, n, 0x4F, &n); - if (!s || n != 5 || memcmp (s, aid, 5)) + if (!s || n != 5 || memcmp (s, piv_aid, 5)) { /* The RID does not match. */ log_error ("piv: missing or invalid DO 0x79.4F in APT\n"); @@ -3317,7 +3488,7 @@ app_select_piv (app_t app) goto leave; } - if (app->cardtype && !strcmp (app->cardtype, "yubikey")) + if (app->card->cardtype == CARDTYPE_YUBIKEY) app->app_local->flags.yubikey = 1; @@ -3327,6 +3498,7 @@ app_select_piv (app_t app) dump_all_do (slot); app->fnc.deinit = do_deinit; + app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; @@ -3340,6 +3512,7 @@ app_select_piv (app_t app) app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_chv; app->fnc.check_pin = do_check_chv; + app->fnc.with_keygrip = do_with_keygrip; leave: diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c index 8094b2463..16d25b581 100644 --- a/scd/app-sc-hsm.c +++ b/scd/app-sc-hsm.c @@ -33,7 +33,6 @@ #include "scdaemon.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "apdu.h" @@ -484,7 +483,8 @@ read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ - err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen, 255); + err = select_and_read_binary (app_get_slot (app), + fid, "PrKDF", &buffer, &buflen, 255); if (err) return err; @@ -832,7 +832,7 @@ read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, xfree (buffer); buffer = NULL; buflen = 0; - err = select_and_read_binary (app->slot, + err = select_and_read_binary (app_get_slot (app), ((SC_HSM_EE_PREFIX << 8) | (fid & 0xFF)), "CertEF", &buffer, &buflen, 1); if (!err && buffer[0] == 0x30) @@ -953,7 +953,8 @@ read_ef_cd (app_t app, unsigned short fid, cdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ - err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen, 255); + err = select_and_read_binary (app_get_slot (app), fid, "CDF", + &buffer, &buflen, 255); if (err) return err; @@ -1202,7 +1203,7 @@ read_serialno(app_t app) size_t n, objlen, hdrlen, chrlen; int class, tag, constructed, ndef; - err = select_and_read_binary (app->slot, 0x2F02, "EF.C_DevAut", + err = select_and_read_binary (app_get_slot (app), 0x2F02, "EF.C_DevAut", &buffer, &buflen, 512); if (err) return err; @@ -1229,15 +1230,15 @@ read_serialno(app_t app) } chrlen -= 5; - app->serialno = xtrymalloc (chrlen); - if (!app->serialno) + app->card->serialno = xtrymalloc (chrlen); + if (!app->card->serialno) { err = gpg_error_from_syserror (); goto leave; } - app->serialnolen = chrlen; - memcpy (app->serialno, chr, chrlen); + app->card->serialnolen = chrlen; + memcpy (app->card->serialno, chr, chrlen); leave: xfree (buffer); @@ -1260,7 +1261,7 @@ read_meta (app_t app) if (err) return err; - err = list_ef (app->slot, &eflist, &eflistlen); + err = list_ef (app_get_slot (app), &eflist, &eflistlen); if (err) return err; @@ -1454,7 +1455,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, return 0; } - err = select_and_read_binary (app->slot, cdf->fid, "CD", + err = select_and_read_binary (app_get_slot (app), cdf->fid, "CD", &buffer, &buflen, 4096); if (err) { @@ -1592,7 +1593,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } else if (!strcmp (name, "$DISPSERIALNO")) { - send_status_info (ctrl, name, app->serialno, app->serialnolen, NULL, 0); + send_status_info (ctrl, name, + app->card->serialno, app->card->serialnolen, NULL, 0); return 0; } @@ -1693,8 +1695,8 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), char *prompt; int sw; - sw = apdu_send_simple (app->slot, 0, 0x00, ISO7816_VERIFY, 0x00, 0x81, - -1, NULL); + sw = apdu_send_simple (app_get_slot (app), + 0, 0x00, ISO7816_VERIFY, 0x00, 0x81, -1, NULL); if (sw == SW_SUCCESS) return 0; /* PIN already verified */ @@ -1719,7 +1721,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), prompt = "||Please enter the PIN"; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { err = pincb (pincb_arg, prompt, NULL); if (err) @@ -1728,7 +1730,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } - err = iso7816_verify_kp (app->slot, 0x81, &pininfo); + err = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else @@ -1740,7 +1742,8 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } - err = iso7816_verify (app->slot, 0x81, pinvalue, strlen(pinvalue)); + err = iso7816_verify (app_get_slot (app), + 0x81, pinvalue, strlen(pinvalue)); xfree (pinvalue); } if (err) @@ -1883,7 +1886,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, if (err) return err; - sw = apdu_send_le (app->slot, 1, 0x80, 0x68, prkdf->key_reference, algoid, + sw = apdu_send_le (app_get_slot (app), + 1, 0x80, 0x68, prkdf->key_reference, algoid, cdsblklen, cdsblk, 0, outdata, outdatalen); return iso7816_map_sw (sw); } @@ -2018,7 +2022,8 @@ do_decipher (app_t app, const char *keyidstr, if (err) return err; - sw = apdu_send_le (app->slot, 1, 0x80, 0x62, prkdf->key_reference, 0x21, + sw = apdu_send_le (app_get_slot (app), + 1, 0x80, 0x62, prkdf->key_reference, 0x21, p1blklen, p1blk, 0, &rspdata, &rspdatalen); err = iso7816_map_sw (sw); if (err) @@ -2044,13 +2049,13 @@ do_decipher (app_t app, const char *keyidstr, gpg_error_t app_select_sc_hsm (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, sc_hsm_aid, sizeof sc_hsm_aid, 0); if (!rc) { - app->apptype = "SC-HSM"; + app->apptype = APPTYPE_SC_HSM; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -2064,6 +2069,7 @@ app_select_sc_hsm (app_t app) goto leave; app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = do_getattr; @@ -26,14 +26,142 @@ #include "scdaemon.h" #include "../common/exechelp.h" -#include "app-common.h" #include "iso7816.h" #include "apdu.h" #include "../common/tlv.h" -static npth_mutex_t app_list_lock; -static app_t app_top; +/* Lock to protect the list of cards and its associated + * applications. */ +static npth_mutex_t card_list_lock; + +/* A list of card contexts. A card is a collection of applications + * (described by app_t) on the same physical token. */ +static card_t card_top; + + +/* The list of application names and their select function. If no + * specific application is selected the first available application on + * a card is selected. */ +struct app_priority_list_s +{ + apptype_t apptype; + char const *name; + gpg_error_t (*select_func)(app_t); +}; + +static struct app_priority_list_s app_priority_list[] = + {{ APPTYPE_OPENPGP , "openpgp", app_select_openpgp }, + { APPTYPE_PIV , "piv", app_select_piv }, + { APPTYPE_NKS , "nks", app_select_nks }, + { APPTYPE_P15 , "p15", app_select_p15 }, + { APPTYPE_GELDKARTE, "geldkarte", app_select_geldkarte }, + { APPTYPE_DINSIG , "dinsig", app_select_dinsig }, + { APPTYPE_SC_HSM , "sc-hsm", app_select_sc_hsm }, + { APPTYPE_NONE , NULL, NULL } + /* APPTYPE_UNDEFINED is special and not listed here. */ + }; + + + + +/* Map a cardtype to a string. Never returns NULL. */ +const char * +strcardtype (cardtype_t t) +{ + switch (t) + { + case CARDTYPE_GENERIC: return "generic"; + case CARDTYPE_YUBIKEY: return "yubikey"; + } + return "?"; +} + + +/* Map an application type to a string. Never returns NULL. */ +const char * +strapptype (apptype_t t) +{ + int i; + + for (i=0; app_priority_list[i].apptype; i++) + if (app_priority_list[i].apptype == t) + return app_priority_list[i].name; + return t == APPTYPE_UNDEFINED? "undefined" : t? "?" : "none"; +} + + +/* Return the apptype for NAME. */ +static apptype_t +apptype_from_name (const char *name) +{ + int i; + + if (!name) + return APPTYPE_NONE; + + for (i=0; app_priority_list[i].apptype; i++) + if (!ascii_strcasecmp (app_priority_list[i].name, name)) + return app_priority_list[i].apptype; + if (!ascii_strcasecmp ("undefined", name)) + return APPTYPE_UNDEFINED; + return APPTYPE_NONE; +} + + +/* Initialization function to change the default app_priority_list. + * LIST is a list of comma or space separated strings with application + * names. Unknown names will only result in warning message. + * Application not mentioned in LIST are used in their original order + * after the given once. */ +void +app_update_priority_list (const char *arg) +{ + struct app_priority_list_s save; + char **names; + int i, j, idx; + + names = strtokenize (arg, ", "); + if (!names) + log_fatal ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + + idx = 0; + for (i=0; names[i]; i++) + { + ascii_strlwr (names[i]); + for (j=0; j < i; j++) + if (!strcmp (names[j], names[i])) + break; + if (j < i) + { + log_info ("warning: duplicate application '%s' in priority list\n", + names[i]); + continue; + } + + for (j=idx; app_priority_list[j].name; j++) + if (!strcmp (names[i], app_priority_list[j].name)) + break; + if (!app_priority_list[j].name) + { + log_info ("warning: unknown application '%s' in priority list\n", + names[i]); + continue; + } + save = app_priority_list[idx]; + app_priority_list[idx] = app_priority_list[j]; + app_priority_list[j] = save; + idx++; + } + log_assert (idx < DIM (app_priority_list)); + + xfree (names); + for (i=0; app_priority_list[i].name; i++) + log_info ("app priority %d: %s\n", i, app_priority_list[i].name); +} + + static void print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) { @@ -48,59 +176,69 @@ print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) } -/* Lock the reader SLOT. This function shall be used right before - calling any of the actual application functions to serialize access - to the reader. We do this always even if the reader is not - actually used. This allows an actual connection to assume that it - never shares a reader (while performing one command). Returns 0 on - success; only then the unlock_reader function must be called after - returning from the handler. */ +/* Lock the CARD. This function shall be used right before calling + * any of the actual application functions to serialize access to the + * reader. We do this always even if the card is not actually used. + * This allows an actual connection to assume that it never shares a + * card (while performing one command). Returns 0 on success; only + * then the unlock_reader function must be called after returning from + * the handler. Right now we assume a that a reader has just one + * card; this may eventually need refinement. */ static gpg_error_t -lock_app (app_t app, ctrl_t ctrl) +lock_card (card_t card, ctrl_t ctrl) { - if (npth_mutex_lock (&app->lock)) + if (npth_mutex_lock (&card->lock)) { gpg_error_t err = gpg_error_from_syserror (); - log_error ("failed to acquire APP lock for %p: %s\n", - app, gpg_strerror (err)); + log_error ("failed to acquire CARD lock for %p: %s\n", + card, gpg_strerror (err)); return err; } - apdu_set_progress_cb (app->slot, print_progress_line, ctrl); - apdu_set_prompt_cb (app->slot, popup_prompt, ctrl); + apdu_set_progress_cb (card->slot, print_progress_line, ctrl); + apdu_set_prompt_cb (card->slot, popup_prompt, ctrl); return 0; } -/* Release a lock on the reader. See lock_reader(). */ + +/* Release a lock on a card. See lock_reader(). */ static void -unlock_app (app_t app) +unlock_card (card_t card) { - apdu_set_progress_cb (app->slot, NULL, NULL); - apdu_set_prompt_cb (app->slot, NULL, NULL); + apdu_set_progress_cb (card->slot, NULL, NULL); + apdu_set_prompt_cb (card->slot, NULL, NULL); - if (npth_mutex_unlock (&app->lock)) + if (npth_mutex_unlock (&card->lock)) { gpg_error_t err = gpg_error_from_syserror (); - log_error ("failed to release APP lock for %p: %s\n", - app, gpg_strerror (err)); + log_error ("failed to release CARD lock for %p: %s\n", + card, gpg_strerror (err)); } } /* This function may be called to print information pertaining to the - current state of this module to the log. */ + * current state of this module to the log. */ void app_dump_state (void) { + card_t c; app_t a; - npth_mutex_lock (&app_list_lock); - for (a = app_top; a; a = a->next) - log_info ("app_dump_state: app=%p type='%s'\n", a, a->apptype); - npth_mutex_unlock (&app_list_lock); + npth_mutex_lock (&card_list_lock); + for (c = card_top; c; c = c->next) + { + log_info ("app_dump_state: card=%p slot=%d type=%s\n", + c, c->slot, strcardtype (c->cardtype)); + for (a=c->app; a; a = a->next) + log_info ("app_dump_state: app=%p type='%s'\n", + a, strapptype (a->apptype)); + } + npth_mutex_unlock (&card_list_lock); } + /* Check whether the application NAME is allowed. This does not mean we have support for it though. */ static int @@ -115,34 +253,71 @@ is_app_allowed (const char *name) } -static gpg_error_t -check_conflict (app_t app, const char *name) +/* This function is mainly used by the serialno command to check for + * an application conflict which may appear if the serialno command is + * used to request a specific application and the connection has + * already done a select_application. Return values are: + * 0 - No conflict + * GPG_ERR_FALSE - Another application is in use but it is possible + * to switch to the requested application. + * Other code - Switching is not possible. + * + * If SERIALNO_BIN is not NULL a coflict is onl asserted if the + * serialno of the card matches. + */ +gpg_error_t +check_application_conflict (card_t card, const char *name, + const unsigned char *serialno_bin, + size_t serialno_bin_len) { - if (!app || !name || (app->apptype && !ascii_strcasecmp (app->apptype, name))) + app_t app; + + if (!card || !name) return 0; + if (!card->app) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); /* Should not happen. */ - if (app->apptype && !strcmp (app->apptype, "UNDEFINED")) + if (serialno_bin && card->serialno) + { + if (serialno_bin_len != card->serialnolen + || memcmp (serialno_bin, card->serialno, card->serialnolen)) + return 0; /* The card does not match the requested S/N. */ + } + + /* Check whether the requested NAME matches any already selected + * application. */ + for (app = card->app; app; app = app->next) + if (!ascii_strcasecmp (strapptype (app->apptype), name)) + return 0; + + if (card->app->apptype == APPTYPE_UNDEFINED) return 0; + if (card->cardtype == CARDTYPE_YUBIKEY) + { + if (card->app->apptype == APPTYPE_OPENPGP) + { + /* Current app is OpenPGP. */ + if (!ascii_strcasecmp (name, "piv")) + return gpg_error (GPG_ERR_FALSE); /* Switching allowed. */ + } + else if (card->app->apptype == APPTYPE_PIV) + { + /* Current app is PIV. */ + if (!ascii_strcasecmp (name, "openpgp")) + return gpg_error (GPG_ERR_FALSE); /* Switching allowed. */ + } + } + log_info ("application '%s' in use - can't switch\n", - app->apptype? app->apptype : "<null>"); + strapptype (card->app->apptype)); return gpg_error (GPG_ERR_CONFLICT); } -/* This function is used by the serialno command to check for an - application conflict which may appear if the serialno command is - used to request a specific application and the connection has - already done a select_application. */ -gpg_error_t -check_application_conflict (const char *name, app_t app) -{ - return check_conflict (app, name); -} - gpg_error_t -app_reset (app_t app, ctrl_t ctrl, int send_reset) +card_reset (card_t card, ctrl_t ctrl, int send_reset) { gpg_error_t err = 0; @@ -150,21 +325,21 @@ app_reset (app_t app, ctrl_t ctrl, int send_reset) { int sw; - lock_app (app, ctrl); - sw = apdu_reset (app->slot); + lock_card (card, ctrl); + sw = apdu_reset (card->slot); if (sw) err = gpg_error (GPG_ERR_CARD_RESET); - app->reset_requested = 1; - unlock_app (app); + card->reset_requested = 1; + unlock_card (card); scd_kick_the_loop (); gnupg_sleep (1); } else { - ctrl->app_ctx = NULL; - release_application (app, 0); + ctrl->card_ctx = NULL; + card_unref (card); } return err; @@ -175,35 +350,37 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, int periodical_check_needed) { gpg_error_t err = 0; + card_t card = NULL; app_t app = NULL; unsigned char *result = NULL; size_t resultlen; int want_undefined; + int i; - /* Need to allocate a new one. */ - app = xtrycalloc (1, sizeof *app); - if (!app) + /* Need to allocate a new card object */ + card = xtrycalloc (1, sizeof *card); + if (!card) { err = gpg_error_from_syserror (); log_info ("error allocating context: %s\n", gpg_strerror (err)); return err; } - app->slot = slot; - app->card_status = (unsigned int)-1; + card->slot = slot; + card->card_status = (unsigned int)-1; - if (npth_mutex_init (&app->lock, NULL)) + if (npth_mutex_init (&card->lock, NULL)) { err = gpg_error_from_syserror (); log_error ("error initializing mutex: %s\n", gpg_strerror (err)); - xfree (app); + xfree (card); return err; } - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) { - xfree (app); + xfree (card); return err; } @@ -221,9 +398,12 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, * config. */ static char const yk_aid[] = { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; /*MGR*/ + static char const otp_aid[] = + { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01 }; /*OTP*/ unsigned char *buf; size_t buflen; - const unsigned char *s0, *s1; + const unsigned char *s0; + unsigned char formfactor; size_t n; if (!iso7816_select_application (slot, yk_aid, sizeof yk_aid, @@ -231,7 +411,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, && !iso7816_apdu_direct (slot, "\x00\x1d\x00\x00\x00", 5, 0, NULL, &buf, &buflen)) { - app->cardtype = "yubikey"; + card->cardtype = CARDTYPE_YUBIKEY; if (opt.verbose) { log_info ("Yubico: config="); @@ -243,29 +423,43 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, if (buflen > 1) { s0 = find_tlv (buf+1, buflen-1, 0x04, &n); /* Form factor */ - if (s0 && n == 1) + formfactor = (s0 && n == 1)? *s0 : 0; + + s0 = find_tlv (buf+1, buflen-1, 0x02, &n); /* Serial */ + if (s0 && n >= 4) { - s1 = find_tlv (buf+1, buflen-1, 0x02, &n); /* Serial */ - if (s1 && n >= 4) + card->serialno = xtrymalloc (3 + 1 + n); + if (card->serialno) { - app->serialno = xtrymalloc (3 + 1 + n); - if (app->serialno) - { - app->serialnolen = 3 + 1 + n; - app->serialno[0] = 0xff; - app->serialno[1] = 0x02; - app->serialno[2] = 0x0; - app->serialno[3] = *s0; - memcpy (app->serialno + 4, s1, n); - /* Note that we do not clear the error - * so that no further serial number - * testing is done. After all we just - * set the serial number. */ - } + card->serialnolen = 3 + 1 + n; + card->serialno[0] = 0xff; + card->serialno[1] = 0x02; + card->serialno[2] = 0x0; + card->serialno[3] = formfactor; + memcpy (card->serialno + 4, s0, n); + /* Note that we do not clear the error + * so that no further serial number + * testing is done. After all we just + * set the serial number. */ } - s1 = find_tlv (buf+1, buflen-1, 0x05, &n); /* version */ - if (s1 && n == 3) - app->cardversion = ((s1[0]<<16)|(s1[1]<<8)|s1[2]); + } + + s0 = find_tlv (buf+1, buflen-1, 0x05, &n); /* version */ + if (s0 && n == 3) + card->cardversion = ((s0[0]<<16)|(s0[1]<<8)|s0[2]); + else if (!s0) + { + /* No version - this is not a Yubikey 5. We now + * switch to the OTP app and take the first + * three bytes of the reponse as version + * number. */ + xfree (buf); + buf = NULL; + if (!iso7816_select_application_ext (slot, + otp_aid, sizeof otp_aid, + 1, &buf, &buflen) + && buflen > 3) + card->cardversion = ((buf[0]<<16)|(buf[1]<<8)|buf[2]); } } xfree (buf); @@ -286,7 +480,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, resultlen -= (p-result); if (p && n > resultlen && n == 0x0d && resultlen+1 == n) { - /* The object it does not fit into the buffer. This is an + /* The object does not fit into the buffer. This is an invalid encoding (or the buffer is too short. However, I have some test cards with such an invalid encoding and therefore I use this ugly workaround to return something @@ -300,9 +494,9 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, /* The GDO file is pretty short, thus we simply reuse it for storing the serial number. */ memmove (result, p, n); - app->serialno = result; - app->serialnolen = n; - err = app_munge_serialno (app); + card->serialno = result; + card->serialnolen = n; + err = app_munge_serialno (card); if (err) goto leave; } @@ -312,12 +506,23 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, } } + /* Allocate a new app object. */ + app = xtrycalloc (1, sizeof *app); + if (!app) + { + err = gpg_error_from_syserror (); + log_info ("error allocating app context: %s\n", gpg_strerror (err)); + goto leave; + } + card->app = app; + app->card = card; + /* Figure out the application to use. */ if (want_undefined) { /* We switch to the "undefined" application only if explicitly requested. */ - app->apptype = "UNDEFINED"; + app->apptype = APPTYPE_UNDEFINED; /* Clear the error so that we don't run through the application * selection chain. */ err = 0; @@ -330,26 +535,18 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, goto leave; /* Set a default error so that we run through the application - * selecion chain. */ + * selection chain. */ err = gpg_error (GPG_ERR_NOT_FOUND); } - if (err && is_app_allowed ("openpgp") - && (!name || !strcmp (name, "openpgp"))) - err = app_select_openpgp (app); - if (err && is_app_allowed ("piv") && (!name || !strcmp (name, "piv"))) - err = app_select_piv (app); - if (err && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) - err = app_select_nks (app); - if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15"))) - err = app_select_p15 (app); - if (err && is_app_allowed ("geldkarte") - && (!name || !strcmp (name, "geldkarte"))) - err = app_select_geldkarte (app); - if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) - err = app_select_dinsig (app); - if (err && is_app_allowed ("sc-hsm") && (!name || !strcmp (name, "sc-hsm"))) - err = app_select_sc_hsm (app); + /* Find the first available app if NAME is NULL or the matching + * NAME but only if that application is also enabled. */ + for (i=0; err && app_priority_list[i].name; i++) + { + if (is_app_allowed (app_priority_list[i].name) + && (!name || !strcmp (name, app_priority_list[i].name))) + err = app_priority_list[i].select_func (app); + } if (err && name && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE) err = gpg_error (GPG_ERR_NOT_SUPPORTED); @@ -362,44 +559,47 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, else log_info ("no supported card application found: %s\n", gpg_strerror (err)); - unlock_app (app); + unlock_card (card); xfree (app); + xfree (card); return err; } - app->periodical_check_needed = periodical_check_needed; - app->next = app_top; - app_top = app; - unlock_app (app); + card->periodical_check_needed = periodical_check_needed; + card->next = card_top; + card_top = card; + + unlock_card (card); return 0; } + /* If called with NAME as NULL, select the best fitting application - and return a context; otherwise select the application with NAME - and return a context. Returns an error code and stores NULL at - R_APP if no application was found or no card is present. */ + * and return its card context; otherwise select the application with + * NAME and return its card context. Returns an error code and stores + * NULL at R_CARD if no application was found or no card is present. */ gpg_error_t -select_application (ctrl_t ctrl, const char *name, app_t *r_app, +select_application (ctrl_t ctrl, const char *name, card_t *r_card, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len) { gpg_error_t err = 0; - app_t a, a_prev = NULL; + card_t card, card_prev = NULL; - *r_app = NULL; + *r_card = NULL; - npth_mutex_lock (&app_list_lock); + npth_mutex_lock (&card_list_lock); - if (scan || !app_top) + if (scan || !card_top) { struct dev_list *l; - int new_app = 0; + int new_card = 0; /* Scan the devices to find new device(s). */ err = apdu_dev_list_start (opt.reader_port, &l); if (err) { - npth_mutex_unlock (&app_list_lock); + npth_mutex_unlock (&card_list_lock); return err; } @@ -408,7 +608,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, int slot; int periodical_check_needed_this; - slot = apdu_open_reader (l, !app_top); + slot = apdu_open_reader (l, !card_top); if (slot < 0) break; @@ -422,7 +622,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, { err = app_new_register (slot, ctrl, name, periodical_check_needed_this); - new_app++; + new_card++; } if (err) @@ -432,76 +632,144 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, apdu_dev_list_finish (l); /* If new device(s), kick the scdaemon loop. */ - if (new_app) + if (new_card) scd_kick_the_loop (); } - for (a = app_top; a; a = a->next) + for (card = card_top; card; card = card->next) { - lock_app (a, ctrl); + lock_card (card, ctrl); if (serialno_bin == NULL) break; - if (a->serialnolen == serialno_bin_len - && !memcmp (a->serialno, serialno_bin, a->serialnolen)) + if (card->serialnolen == serialno_bin_len + && !memcmp (card->serialno, serialno_bin, card->serialnolen)) break; - unlock_app (a); - a_prev = a; + unlock_card (card); + card_prev = card; } - if (a) + if (card) { - err = check_conflict (a, name); + err = check_application_conflict (card, name, NULL, 0); if (!err) { - a->ref_count++; - *r_app = a; - if (a_prev) + /* Note: We do not use card_ref as we are already locked. */ + card->ref_count++; + *r_card = card; + if (card_prev) { - a_prev->next = a->next; - a->next = app_top; - app_top = a; + card_prev->next = card->next; + card->next = card_top; + card_top = card; } - } - unlock_app (a); + + ctrl->current_apptype = card->app ? card->app->apptype : 0; + } + unlock_card (card); } else err = gpg_error (GPG_ERR_ENODEV); - npth_mutex_unlock (&app_list_lock); + npth_mutex_unlock (&card_list_lock); return err; } +/* This function needs to be called with the NAME of the new + * application to be selected on CARD. On success the application is + * added to the list of the card's active applications as currently + * active application. On error no new application is allocated. + * Selecting an already selected application has no effect. */ +gpg_error_t +select_additional_application (ctrl_t ctrl, const char *name) +{ + gpg_error_t err = 0; + apptype_t req_apptype; + card_t card; + app_t app = NULL; + int i; + + req_apptype = apptype_from_name (name); + if (!req_apptype) + err = gpg_error (GPG_ERR_NOT_FOUND); + + card = ctrl->card_ctx; + if (!card) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + + err = lock_card (card, ctrl); + if (err) + return err; + + /* Check that the requested app has not yet been put onto the list. */ + for (app = card->app; app; app = app->next) + if (app->apptype == req_apptype) + { + /* We already got this one. Note that in this case we don't + * make it the current one but it doesn't matter because + * maybe_switch_app will do that anyway. */ + err = 0; + app = NULL; + goto leave; + } + app = NULL; + + /* Allocate a new app object. */ + app = xtrycalloc (1, sizeof *app); + if (!app) + { + err = gpg_error_from_syserror (); + log_info ("error allocating app context: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Find the app and run the select. */ + for (i=0; app_priority_list[i].apptype; i++) + { + if (app_priority_list[i].apptype == req_apptype + && is_app_allowed (app_priority_list[i].name)) + err = app_priority_list[i].select_func (app); + } + if (!app_priority_list[i].apptype + || (err && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (err) + goto leave; + + /* Add this app. We make it the current one to avoid an extra + * reselect by maybe_switch_app after the select we just did. */ + app->next = card->app; + card->app = app; + ctrl->current_apptype = app->apptype; + log_error ("added app '%s' to the card context\n", strapptype(app->apptype)); + + leave: + unlock_card (card); + xfree (app); + return err; +} + + char * get_supported_applications (void) { - const char *list[] = { - "openpgp", - "piv", - "nks", - "p15", - "geldkarte", - "dinsig", - "sc-hsm", - /* Note: "undefined" is not listed here because it needs special - treatment by the client. */ - NULL - }; int idx; size_t nbytes; char *buffer, *p; + const char *s; - for (nbytes=1, idx=0; list[idx]; idx++) - nbytes += strlen (list[idx]) + 1 + 1; + for (nbytes=1, idx=0; (s=app_priority_list[idx].name); idx++) + nbytes += strlen (s) + 1 + 1; buffer = xtrymalloc (nbytes); if (!buffer) return NULL; - for (p=buffer, idx=0; list[idx]; idx++) - if (is_app_allowed (list[idx])) - p = stpcpy (stpcpy (p, list[idx]), ":\n"); + for (p=buffer, idx=0; (s=app_priority_list[idx].name); idx++) + if (is_app_allowed (s)) + p = stpcpy (stpcpy (p, s), ":\n"); *p = 0; return buffer; @@ -510,62 +778,89 @@ get_supported_applications (void) /* Deallocate the application. */ static void -deallocate_app (app_t app) +deallocate_card (card_t card) { - app_t a, a_prev = NULL; + card_t c, c_prev = NULL; + app_t a, anext; - for (a = app_top; a; a = a->next) - if (a == app) + for (c = card_top; c; c = c->next) + if (c == card) { - if (a_prev == NULL) - app_top = a->next; + if (c_prev == NULL) + card_top = c->next; else - a_prev->next = a->next; + c_prev->next = c->next; break; } else - a_prev = a; + c_prev = c; - if (app->ref_count) - log_error ("trying to release context used yet (%d)\n", app->ref_count); + if (card->ref_count) + log_error ("releasing still used card context (%d)\n", card->ref_count); - if (app->fnc.deinit) + for (a = card->app; a; a = anext) { - app->fnc.deinit (app); - app->fnc.deinit = NULL; + if (a->fnc.deinit) + { + a->fnc.deinit (a); + a->fnc.deinit = NULL; + } + anext = a->next; + xfree (a); } - xfree (app->serialno); + scd_clear_current_app (card); - unlock_app (app); - xfree (app); + xfree (card->serialno); + unlock_card (card); + xfree (card); +} + + +/* Increment the reference counter of CARD. Returns CARD. */ +card_t +card_ref (card_t card) +{ + lock_card (card, NULL); + ++card->ref_count; + unlock_card (card); + return card; } -/* Free the resources associated with the application APP. APP is - allowed to be NULL in which case this is a no-op. Note that we are - using reference counting to track the users of the application and - actually deferring the deallocation to allow for a later reuse by - a new connection. */ + +/* Decrement the reference counter for CARD. Note that we are using + * reference counting to track the users of the card's application and + * are deferring the actual deallocation to allow for a later reuse by + * a new connection. Using NULL for CARD is a no-op. */ void -release_application (app_t app, int locked_already) +card_unref (card_t card) { - if (!app) + if (!card) return; - /* We don't deallocate app here. Instead, we keep it. This is + /* We don't deallocate CARD here. Instead, we keep it. This is useful so that a card does not get reset even if only one session is using the card - this way the PIN cache and other cached data are preserved. */ - if (!locked_already) - lock_app (app, NULL); + lock_card (card, NULL); + card_unref_locked (card); + unlock_card (card); +} + + +/* This is the same as card_unref but assumes that CARD is already + * locked. */ +void +card_unref_locked (card_t card) +{ + if (!card) + return; - if (!app->ref_count) - log_bug ("trying to release an already released context\n"); + if (!card->ref_count) + log_bug ("tried to release an already released card context\n"); - --app->ref_count; - if (!locked_already) - unlock_app (app); + --card->ref_count; } @@ -585,30 +880,30 @@ release_application (app_t app, int locked_already) All other serial number not starting with FF are used as they are. */ gpg_error_t -app_munge_serialno (app_t app) +app_munge_serialno (card_t card) { - if (app->serialnolen && app->serialno[0] == 0xff) + if (card->serialnolen && card->serialno[0] == 0xff) { /* The serial number starts with our special prefix. This requires that we put our default prefix "FF0000" in front. */ - unsigned char *p = xtrymalloc (app->serialnolen + 3); + unsigned char *p = xtrymalloc (card->serialnolen + 3); if (!p) return gpg_error_from_syserror (); memcpy (p, "\xff\0", 3); - memcpy (p+3, app->serialno, app->serialnolen); - app->serialnolen += 3; - xfree (app->serialno); - app->serialno = p; + memcpy (p+3, card->serialno, card->serialnolen); + card->serialnolen += 3; + xfree (card->serialno); + card->serialno = p; } - else if (!app->serialnolen) + else if (!card->serialnolen) { unsigned char *p = xtrymalloc (3); if (!p) return gpg_error_from_syserror (); memcpy (p, "\xff\x7f", 3); - app->serialnolen = 3; - xfree (app->serialno); - app->serialno = p; + card->serialnolen = 3; + xfree (card->serialno); + card->serialno = p; } return 0; } @@ -619,52 +914,129 @@ app_munge_serialno (app_t app) returned as a malloced string (hex encoded) in SERIAL. Caller must free SERIAL unless the function returns an error. */ char * -app_get_serialno (app_t app) +card_get_serialno (card_t card) { char *serial; - if (!app) + if (!card) return NULL; - if (!app->serialnolen) + if (!card->serialnolen) serial = xtrystrdup ("FF7F00"); else - serial = bin2hex (app->serialno, app->serialnolen, NULL); + serial = bin2hex (card->serialno, card->serialnolen, NULL); return serial; } +/* Same as card_get_serialno but takes an APP object. */ +char * +app_get_serialno (app_t app) +{ + if (!app || !app->card) + return NULL; + return card_get_serialno (app->card); +} + + +/* Check that the card has been initialized and whether we need to + * switch to another application on the same card. Switching means + * that the new active app will be moved to the head of the list at + * CARD->app. Thus function must be called with the card lock held. */ +static gpg_error_t +maybe_switch_app (ctrl_t ctrl, card_t card) +{ + gpg_error_t err; + app_t app, app_prev; + + if (!card->ref_count || !card->app) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!ctrl->current_apptype) + { + /* For whatever reasons the current apptype has not been set - + * fix that and use the current app. */ + ctrl->current_apptype = card->app->apptype; + return 0; + } + log_debug ("card=%p want=%s card->app=%s\n", + card, strapptype (ctrl->current_apptype), + strapptype (card->app->apptype)); + app_dump_state (); + if (ctrl->current_apptype == card->app->apptype) + return 0; /* No need to switch. */ + app_prev = card->app; + for (app = app_prev->next; app; app_prev = app, app = app->next) + if (app->apptype == ctrl->current_apptype) + break; + if (!app) + return gpg_error (GPG_ERR_WRONG_CARD); + + if (!app->fnc.reselect) + { + log_error ("oops: reselect function missing for '%s'\n", + strapptype(app->apptype)); + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + } + err = app->fnc.reselect (app, ctrl); + if (err) + { + log_error ("error re-selecting '%s': %s\n", + strapptype(app->apptype), gpg_strerror (err)); + return err; + } + /* Swap APP with the head of the app list. Note that APP is not the + * head of the list. */ + app_prev->next = app->next; + app->next = card->app; + card->app = app; + ctrl->current_apptype = app->apptype; + log_error ("switched to '%s'\n", strapptype(app->apptype)); + + return 0; +} + /* Write out the application specific status lines for the LEARN command. */ gpg_error_t -app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) +app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; + app_t app; - if (!app) + if (!card) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->fnc.learn_status) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - /* We do not send CARD and APPTYPE if only keypairinfo is requested. */ - if (!(flags &1)) + err = lock_card (card, ctrl); + if (err) + return err; + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.learn_status) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else { - if (app->cardtype) - send_status_direct (ctrl, "CARDTYPE", app->cardtype); - if (app->cardversion) - send_status_printf (ctrl, "CARDVERSION", "%X", app->cardversion); - if (app->apptype) - send_status_direct (ctrl, "APPTYPE", app->apptype); - if (app->appversion) - send_status_printf (ctrl, "APPVERSION", "%X", app->appversion); + app = card->app; + + /* We do not send CARD and APPTYPE if only keypairinfo is requested. */ + if (!(flags &1)) + { + if (card->cardtype) + send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); + if (card->cardversion) + send_status_printf (ctrl, "CARDVERSION", "%X", card->cardversion); + if (app->apptype) + send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype)); + if (app->appversion) + send_status_printf (ctrl, "APPVERSION", "%X", app->appversion); + /* FIXME: Send info for the other active apps of the card? */ + } + + err = app->fnc.learn_status (app, ctrl, flags); } - err = lock_app (app, ctrl); - if (err) - return err; - err = app->fnc.learn_status (app, ctrl, flags); - unlock_app (app); + unlock_card (card); return err; } @@ -674,35 +1046,39 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) buffer put into CERT and the length of the certificate put into CERTLEN. */ gpg_error_t -app_readcert (app_t app, ctrl_t ctrl, const char *certid, +app_readcert (card_t card, ctrl_t ctrl, const char *certid, unsigned char **cert, size_t *certlen) { gpg_error_t err; - if (!app) + if (!card) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.readcert) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.readcert (app, certid, cert, certlen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.readcert) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.readcert (card->app, certid, cert, certlen); + + unlock_card (card); return err; } /* Read the key with ID KEYID. On success a canonical encoded - S-expression with the public key will get stored at PK and its - length (for assertions) at PKLEN; the caller must release that - buffer. On error NULL will be stored at PK and PKLEN and an error - code returned. - - This function might not be supported by all applications. */ + * S-expression with the public key will get stored at PK and its + * length (for assertions) at PKLEN; the caller must release that + * buffer. On error NULL will be stored at PK and PKLEN and an error + * code returned. If the key is not required NULL may be passed for + * PK; this makse send if the APP_READKEY_FLAG_INFO has also been set. + * + * This function might not be supported by all applications. */ gpg_error_t -app_readkey (app_t app, ctrl_t ctrl, const char *keyid, +app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags, unsigned char **pk, size_t *pklen) { gpg_error_t err; @@ -712,93 +1088,102 @@ app_readkey (app_t app, ctrl_t ctrl, const char *keyid, if (pklen) *pklen = 0; - if (!app || !keyid || !pk || !pklen) + if (!card || !keyid) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.readkey) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err= app->fnc.readkey (app, keyid, pk, pklen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.readkey) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.readkey (card->app, ctrl, keyid, flags, pk, pklen); + + unlock_card (card); return err; } /* Perform a GETATTR operation. */ gpg_error_t -app_getattr (app_t app, ctrl_t ctrl, const char *name) +app_getattr (card_t card, ctrl_t ctrl, const char *name) { gpg_error_t err; - if (!app || !name || !*name) + if (!card || !name || !*name) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + err = lock_card (card, ctrl); + if (err) + return err; - if (app->cardtype && name && !strcmp (name, "CARDTYPE")) + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (name && !strcmp (name, "CARDTYPE")) { - send_status_direct (ctrl, "CARDTYPE", app->cardtype); - return 0; + send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); } - if (app->apptype && name && !strcmp (name, "APPTYPE")) + else if (name && !strcmp (name, "APPTYPE")) { - send_status_direct (ctrl, "APPTYPE", app->apptype); - return 0; + send_status_direct (ctrl, "APPTYPE", strapptype (card->app->apptype)); } - if (name && !strcmp (name, "SERIALNO")) + else if (name && !strcmp (name, "SERIALNO")) { char *serial; - serial = app_get_serialno (app); + serial = card_get_serialno (card); if (!serial) - return gpg_error (GPG_ERR_INV_VALUE); - - send_status_direct (ctrl, "SERIALNO", serial); - xfree (serial); - return 0; + err = gpg_error (GPG_ERR_INV_VALUE); + else + { + send_status_direct (ctrl, "SERIALNO", serial); + xfree (serial); + } } + else if (!card->app->fnc.getattr) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.getattr (card->app, ctrl, name); - if (!app->fnc.getattr) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); - if (err) - return err; - err = app->fnc.getattr (app, ctrl, name); - unlock_app (app); + unlock_card (card); return err; } + /* Perform a SETATTR operation. */ gpg_error_t -app_setattr (app_t app, ctrl_t ctrl, const char *name, +app_setattr (card_t card, ctrl_t ctrl, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { gpg_error_t err; - if (!app || !name || !*name || !value) + if (!card || !name || !*name || !value) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.setattr) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.setattr) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.setattr (card->app, name, pincb, pincb_arg, + value, valuelen); + + unlock_card (card); return err; } + /* Create the signature and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ gpg_error_t -app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, +app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, @@ -806,31 +1191,35 @@ app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, { gpg_error_t err; - if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.sign) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.sign (app, keyidstr, hashalgo, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.sign) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.sign (card->app, keyidstr, hashalgo, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + + unlock_card (card); if (opt.verbose) log_info ("operation sign result: %s\n", gpg_strerror (err)); return err; } + /* Create the signature using the INTERNAL AUTHENTICATE command and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ gpg_error_t -app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, +app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, @@ -838,20 +1227,23 @@ app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, { gpg_error_t err; - if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.auth) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.auth (app, keyidstr, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.auth) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.auth (card->app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + + unlock_card (card); if (opt.verbose) log_info ("operation auth result: %s\n", gpg_strerror (err)); return err; @@ -862,7 +1254,7 @@ app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ gpg_error_t -app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, +app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, @@ -873,21 +1265,24 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, *r_info = 0; - if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.decipher) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.decipher (app, keyidstr, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen, - r_info); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.decipher) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.decipher (card->app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen, + r_info); + + unlock_card (card); if (opt.verbose) log_info ("operation decipher result: %s\n", gpg_strerror (err)); return err; @@ -896,26 +1291,29 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, /* Perform the WRITECERT operation. */ gpg_error_t -app_writecert (app_t app, ctrl_t ctrl, - const char *certidstr, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - const unsigned char *data, size_t datalen) +app_writecert (card_t card, ctrl_t ctrl, + const char *certidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *data, size_t datalen) { gpg_error_t err; - if (!app || !certidstr || !*certidstr || !pincb) + if (!card || !certidstr || !*certidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.writecert) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.writecert (app, ctrl, certidstr, - pincb, pincb_arg, data, datalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.writecert) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.writecert (card->app, ctrl, certidstr, + pincb, pincb_arg, data, datalen); + + unlock_card (card); if (opt.verbose) log_info ("operation writecert result: %s\n", gpg_strerror (err)); return err; @@ -924,7 +1322,7 @@ app_writecert (app_t app, ctrl_t ctrl, /* Perform the WRITEKEY operation. */ gpg_error_t -app_writekey (app_t app, ctrl_t ctrl, +app_writekey (card_t card, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, @@ -932,45 +1330,51 @@ app_writekey (app_t app, ctrl_t ctrl, { gpg_error_t err; - if (!app || !keyidstr || !*keyidstr || !pincb) + if (!card || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.writekey) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.writekey (app, ctrl, keyidstr, flags, - pincb, pincb_arg, keydata, keydatalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.writekey) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.writekey (card->app, ctrl, keyidstr, flags, + pincb, pincb_arg, keydata, keydatalen); + + unlock_card (card); if (opt.verbose) log_info ("operation writekey result: %s\n", gpg_strerror (err)); return err; } -/* Perform a SETATTR operation. */ +/* Perform a GENKEY operation. */ gpg_error_t -app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, +app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - if (!app || !keynostr || !*keynostr || !pincb) + if (!card || !keynostr || !*keynostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.genkey) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.genkey (app, ctrl, keynostr, keytype, flags, - createtime, pincb, pincb_arg); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.genkey) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.genkey (card->app, ctrl, keynostr, keytype, flags, + createtime, pincb, pincb_arg); + + unlock_card (card); if (opt.verbose) log_info ("operation genkey result: %s\n", gpg_strerror (err)); return err; @@ -981,44 +1385,51 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, directly accesses the card without any application specific wrapper. */ gpg_error_t -app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, unsigned char *buffer) +app_get_challenge (card_t card, ctrl_t ctrl, + size_t nbytes, unsigned char *buffer) { gpg_error_t err; - if (!app || !nbytes || !buffer) + if (!card || !nbytes || !buffer) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = iso7816_get_challenge (app->slot, nbytes, buffer); - unlock_app (app); + + if (!card->ref_count) + err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + else + err = iso7816_get_challenge (card->slot, nbytes, buffer); + + unlock_card (card); return err; } - /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */ gpg_error_t -app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, +app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - if (!app || !chvnostr || !*chvnostr || !pincb) + if (!card || !chvnostr || !*chvnostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.change_pin) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.change_pin (app, ctrl, chvnostr, flags, pincb, pincb_arg); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.change_pin) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.change_pin (card->app, ctrl, + chvnostr, flags, pincb, pincb_arg); + + unlock_card (card); if (opt.verbose) log_info ("operation change_pin result: %s\n", gpg_strerror (err)); return err; @@ -1029,28 +1440,32 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, be used to initialize a the PIN cache for long lasting other operations. Its use is highly application dependent. */ gpg_error_t -app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, +app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - if (!app || !keyidstr || !*keyidstr || !pincb) + if (!card || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.check_pin) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.check_pin) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.check_pin (card->app, keyidstr, pincb, pincb_arg); + + unlock_card (card); if (opt.verbose) log_info ("operation check_pin result: %s\n", gpg_strerror (err)); return err; } + static void report_change (int slot, int old_status, int cur_status) { @@ -1110,26 +1525,27 @@ report_change (int slot, int old_status, int cur_status) xfree (homestr); } + int scd_update_reader_status_file (void) { - app_t a, app_next; + card_t card, card_next; int periodical_check_needed = 0; - npth_mutex_lock (&app_list_lock); - for (a = app_top; a; a = app_next) + npth_mutex_lock (&card_list_lock); + for (card = card_top; card; card = card_next) { int sw; unsigned int status; - lock_app (a, NULL); - app_next = a->next; + lock_card (card, NULL); + card_next = card->next; - if (a->reset_requested) + if (card->reset_requested) status = 0; else { - sw = apdu_get_status (a->slot, 0, &status); + sw = apdu_get_status (card->slot, 0, &status); if (sw == SW_HOST_NO_READER) { /* Most likely the _reader_ has been unplugged. */ @@ -1138,40 +1554,41 @@ scd_update_reader_status_file (void) else if (sw) { /* Get status failed. Ignore that. */ - if (a->periodical_check_needed) + if (card->periodical_check_needed) periodical_check_needed = 1; - unlock_app (a); + unlock_card (card); continue; } } - if (a->card_status != status) + if (card->card_status != status) { - report_change (a->slot, a->card_status, status); - send_client_notifications (a, status == 0); + report_change (card->slot, card->card_status, status); + send_client_notifications (card, status == 0); if (status == 0) { - log_debug ("Removal of a card: %d\n", a->slot); - apdu_close_reader (a->slot); - deallocate_app (a); + log_debug ("Removal of a card: %d\n", card->slot); + apdu_close_reader (card->slot); + deallocate_card (card); } else { - a->card_status = status; - if (a->periodical_check_needed) + card->card_status = status; + if (card->periodical_check_needed) periodical_check_needed = 1; - unlock_app (a); + unlock_card (card); } } else { - if (a->periodical_check_needed) + if (card->periodical_check_needed) periodical_check_needed = 1; - unlock_app (a); + unlock_card (card); } } - npth_mutex_unlock (&app_list_lock); + + npth_mutex_unlock (&card_list_lock); return periodical_check_needed; } @@ -1185,7 +1602,7 @@ initialize_module_command (void) { gpg_error_t err; - if (npth_mutex_init (&app_list_lock, NULL)) + if (npth_mutex_init (&card_list_lock, NULL)) { err = gpg_error_from_syserror (); log_error ("app: error initializing mutex: %s\n", gpg_strerror (err)); @@ -1195,20 +1612,115 @@ initialize_module_command (void) return apdu_init (); } -void + +/* Sort helper for app_send_card_list. */ +static int +compare_card_list_items (const void *arg_a, const void *arg_b) +{ + const card_t a = *(const card_t *)arg_a; + const card_t b = *(const card_t *)arg_b; + + return a->slot - b->slot; +} + + +/* Send status lines with the serialno of all inserted cards. */ +gpg_error_t app_send_card_list (ctrl_t ctrl) { - app_t a; + gpg_error_t err; + card_t c; char buf[65]; + card_t *cardlist = NULL; + int n, ncardlist; + + npth_mutex_lock (&card_list_lock); + for (n=0, c = card_top; c; c = c->next) + n++; + cardlist = xtrycalloc (n, sizeof *cardlist); + if (!cardlist) + { + err = gpg_error_from_syserror (); + goto leave; + } + for (ncardlist=0, c = card_top; c; c = c->next) + cardlist[ncardlist++] = c; + qsort (cardlist, ncardlist, sizeof *cardlist, compare_card_list_items); - npth_mutex_lock (&app_list_lock); - for (a = app_top; a; a = a->next) + for (n=0; n < ncardlist; n++) { - if (DIM (buf) < 2 * a->serialnolen + 1) + if (DIM (buf) < 2 * cardlist[n]->serialnolen + 1) continue; - bin2hex (a->serialno, a->serialnolen, buf); + bin2hex (cardlist[n]->serialno, cardlist[n]->serialnolen, buf); send_status_direct (ctrl, "SERIALNO", buf); } - npth_mutex_unlock (&app_list_lock); + + err = 0; + + leave: + npth_mutex_unlock (&card_list_lock); + xfree (cardlist); + return err; +} + + +/* Execute an action for each app. ACTION can be one of: + * + * - KEYGRIP_ACTION_SEND_DATA + * + * If KEYGRIP_STR matches a public key of any active application + * send information as LF terminated data lines about the public + * key. The format of these lines is + * <keygrip> T <serialno> <idstr> + * If a match was found a pointer to the matching application is + * returned. With the KEYGRIP_STR given as NULL, lines for all + * keys will be send and the return value is NULL. + * + * - KEYGRIP_ACTION_WRITE_STATUS + * + * Same as KEYGRIP_ACTION_SEND_DATA but uses status lines instead + * of data lines. + * + * - KEYGRIP_ACTION_LOOKUP + * + * Returns a pointer to the application matching KEYGRIP_STR but + * does not emit any status or data lines. If no key with that + * keygrip is available or KEYGRIP_STR is NULL, NULL is returned. + */ +card_t +app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str) +{ + gpg_error_t err; + card_t c; + app_t a; + + npth_mutex_lock (&card_list_lock); + + for (c = card_top; c; c = c->next) + for (a = c->app; a; a = a->next) + if (a->fnc.with_keygrip) + { + if (!lock_card (c, ctrl)) + { + err = a->fnc.with_keygrip (a, ctrl, action, keygrip_str); + unlock_card (c); + if (!err) + goto leave_the_loop; + } + } + + leave_the_loop: + + /* FIXME: Add app switching logic. The above code assumes that the + * actions can be performend without switching. This needs to be + * checked. */ + + /* Force switching of the app if the selected one is not the current + * one. Changing the current apptype is sufficient to do this. */ + if (c && c->app && c->app->apptype != a->apptype) + ctrl->current_apptype = a->apptype; + + npth_mutex_unlock (&card_list_lock); + return c; } diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 69df17355..d762490c8 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -110,26 +110,12 @@ /* CCID command timeout. */ #define CCID_CMD_TIMEOUT (5*1000) -/* OpenPGPcard v2.1 requires huge timeout for key generation. */ -#define CCID_CMD_TIMEOUT_LONGER (60*1000) /* Depending on how this source is used we either define our error - output to go to stderr or to the GnuPG based logging functions. We - use the latter when GNUPG_MAJOR_VERSION or GNUPG_SCD_MAIN_HEADER - are defined. */ -#if defined(GNUPG_MAJOR_VERSION) || defined(GNUPG_SCD_MAIN_HEADER) - -#if defined(GNUPG_SCD_MAIN_HEADER) -# include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ -# include "options.h" -# include "util.h" -# include "memory.h" -# include "cardglue.h" -# else /* This is the modularized GnuPG 1.9 or later. */ + * output to go to stderr or to the GnuPG based logging functions. We + * use the latter when GNUPG_MAJOR_VERSION is defined. */ +#if defined(GNUPG_MAJOR_VERSION) # include "scdaemon.h" -#endif - # define DEBUGOUT(t) do { if (debug_level) \ log_debug (DRVNAME t); } while (0) @@ -175,7 +161,7 @@ # define DEBUGOUT_LF() do { if (debug_level) \ putc ('\n', stderr); } while (0) -#endif /* This source not used by scdaemon. */ +#endif /* This source is not used by scdaemon. */ #ifndef EAGAIN @@ -960,7 +946,7 @@ get_escaped_usb_string (libusb_device_handle *idev, int idx, rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8), 0, - (char*)buf, sizeof buf, 1000 /* ms timeout */); + buf, sizeof buf, 1000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); #endif @@ -975,7 +961,7 @@ get_escaped_usb_string (libusb_device_handle *idev, int idx, rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) + idx, langid, - (char*)buf, sizeof buf, 1000 /* ms timeout */); + buf, sizeof buf, 1000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); #endif @@ -1490,7 +1476,9 @@ intr_cb (struct libusb_transfer *transfer) { DEBUGOUT ("CCID: card removed\n"); handle->powered_off = 1; +#if defined(GNUPG_MAJOR_VERSION) scd_kick_the_loop (); +#endif } else { @@ -1505,7 +1493,9 @@ intr_cb (struct libusb_transfer *transfer) device_removed: DEBUGOUT ("CCID: device removed\n"); handle->powered_off = 1; +#if defined(GNUPG_MAJOR_VERSION) scd_kick_the_loop (); +#endif } } @@ -1731,31 +1721,37 @@ ccid_require_get_status (ccid_driver_t handle) return 1; } - -static void -do_close_reader (ccid_driver_t handle) +static int +send_power_off (ccid_driver_t handle) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; - if (!handle->powered_off) - { - msg[0] = PC_to_RDR_IccPowerOff; - msg[5] = 0; /* slot */ - msg[6] = seqno = handle->seqno++; - msg[7] = 0; /* RFU */ - msg[8] = 0; /* RFU */ - msg[9] = 0; /* RFU */ - set_msg_len (msg, 0); - msglen = 10; + msg[0] = PC_to_RDR_IccPowerOff; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + msglen = 10; - rc = bulk_out (handle, msg, msglen, 0); - if (!rc) - bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, - seqno, 2000, 0); - } + rc = bulk_out (handle, msg, msglen, 0); + if (!rc) + bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, + seqno, 2000, 0); + return rc; +} + +static void +do_close_reader (ccid_driver_t handle) +{ + int rc; + + if (!handle->powered_off) + send_power_off (handle); if (handle->transfer) { @@ -1907,7 +1903,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out, - (char*)msg, msglen, &transferred, + msg, msglen, &transferred, 5000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); @@ -1954,7 +1950,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in, - (char*)buffer, length, &msglen, timeout); + buffer, length, &msglen, timeout); #ifdef USE_NPTH npth_protect (); #endif @@ -2074,7 +2070,9 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, { DEBUGOUT ("CCID: card inactive/removed\n"); handle->powered_off = 1; +#if defined(GNUPG_MAJOR_VERSION) scd_kick_the_loop (); +#endif } } @@ -2088,7 +2086,7 @@ static int abort_cmd (ccid_driver_t handle, int seqno) { int rc; - char dummybuf[8]; + unsigned char dummybuf[8]; unsigned char msg[100]; int msglen; @@ -2139,7 +2137,7 @@ abort_cmd (ccid_driver_t handle, int seqno) npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out, - (char*)msg, msglen, &transferred, + msg, msglen, &transferred, 5000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); @@ -2157,7 +2155,7 @@ abort_cmd (ccid_driver_t handle, int seqno) npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in, - (char*)msg, sizeof msg, &msglen, + msg, sizeof msg, &msglen, 5000 /*ms timeout*/); #ifdef USE_NPTH npth_protect (); @@ -2278,7 +2276,7 @@ ccid_poll (ccid_driver_t handle) int i, j; rc = libusb_interrupt_transfer (handle->idev, handle->ep_intr, - (char*)msg, sizeof msg, &msglen, + msg, sizeof msg, &msglen, 0 /* ms timeout */ ); if (rc == LIBUSB_ERROR_TIMEOUT) return 0; @@ -2611,6 +2609,21 @@ ccid_get_atr (ccid_driver_t handle, NULL, 0, NULL)) goto again; } + else if (statusbits == 0 && CCID_COMMAND_FAILED (msg)) + { + /* Card was active already, and something went wrong with + PC_to_RDR_IccPowerOn command. It may be baud-rate mismatch + between the card and the reader. To recover from this state, + send PC_to_RDR_IccPowerOff command to reset the card and try + again. + */ + rc = send_power_off (handle); + if (rc) + return rc; + + statusbits = 1; + goto again; + } else if (CCID_COMMAND_FAILED (msg)) return CCID_DRIVER_ERR_CARD_IO_ERROR; @@ -2977,7 +2990,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle, bit 7 1 bit 6 1 bit 5 clear=request,set=response - bit 4..0 0 = resyncronisation request + bit 4..0 0 = resynchronization request 1 = information field size request 2 = abort request 3 = extension of BWT request @@ -3094,7 +3107,7 @@ ccid_transceive (ccid_driver_t handle, msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; - msg[7] = 4; /* bBWI */ + msg[7] = (wait_more ? wait_more : 1); /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); @@ -3120,7 +3133,7 @@ ccid_transceive (ccid_driver_t handle, msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, seqno, - wait_more? CCID_CMD_TIMEOUT_LONGER: CCID_CMD_TIMEOUT, 0); + (wait_more ? wait_more : 1) * CCID_CMD_TIMEOUT, 0); if (rc) return rc; @@ -3150,6 +3163,7 @@ ccid_transceive (ccid_driver_t handle, (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)? " [more]":"")); + wait_more = 0; if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ retries = 0; @@ -3295,9 +3309,7 @@ ccid_transceive (ccid_driver_t handle, /* Wait time extension request. */ unsigned char bwi = tpdu[3]; - /* Check if it's unusual value which can't be expressed in ATR. */ - if (bwi > 15) - wait_more = 1; + wait_more = bwi; msg = send_buffer; tpdu = msg + hdrlen; @@ -3726,7 +3738,7 @@ print_result (int rc, const unsigned char *data, size_t length) int main (int argc, char **argv) { - int rc; + gpg_error_t err; ccid_driver_t ccid; int slotstat; unsigned char result[512]; @@ -3735,6 +3747,8 @@ main (int argc, char **argv) int verify_123456 = 0; int did_verify = 0; int no_poll = 0; + int idx_max; + struct ccid_dev_table *ccid_table; if (argc) { @@ -3778,27 +3792,36 @@ main (int argc, char **argv) break; } - rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL); - if (rc) + err = ccid_dev_scan (&idx_max, &ccid_table); + if (err) + return 1; + + if (idx_max == 0) + return 1; + + err = ccid_open_reader (argc? *argv:NULL, 0, ccid_table, &ccid, NULL); + if (err) return 1; + ccid_dev_scan_finish (ccid_table, idx_max); + if (!no_poll) ccid_poll (ccid); fputs ("getting ATR ...\n", stderr); - rc = ccid_get_atr (ccid, NULL, 0, NULL); - if (rc) + err = ccid_get_atr (ccid, NULL, 0, NULL); + if (err) { - print_error (rc); + print_error (err); return 1; } if (!no_poll) ccid_poll (ccid); fputs ("getting slot status ...\n", stderr); - rc = ccid_slot_status (ccid, &slotstat, 1); - if (rc) + err = ccid_slot_status (ccid, &slotstat, 1); + if (err) { - print_error (rc); + print_error (err); return 1; } @@ -3809,10 +3832,10 @@ main (int argc, char **argv) { static unsigned char apdu[] = { 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01}; - rc = ccid_transceive (ccid, - apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, + apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } @@ -3822,9 +3845,9 @@ main (int argc, char **argv) fputs ("getting OpenPGP DO 0x65 ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 }; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } if (!no_pinpad) @@ -3834,22 +3857,18 @@ main (int argc, char **argv) if (!no_pinpad) { static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; + pininfo_t pininfo = { 0, 0, 0 }; - - if (ccid_transceive_secure (ccid, - apdu, sizeof apdu, - 1, 0, 0, 0, + if (ccid_transceive_secure (ccid, apdu, sizeof apdu, &pininfo, NULL, 0, NULL)) fputs ("can't verify using a PIN-Pad reader\n", stderr); else { - fputs ("verifying CHV1 using the PINPad ....\n", stderr); + fputs ("verifying CHV1 using the PINPad ....\n", stderr); - rc = ccid_transceive_secure (ccid, - apdu, sizeof apdu, - 1, 0, 0, 0, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive_secure (ccid, apdu, sizeof apdu, &pininfo, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); did_verify = 1; } } @@ -3860,20 +3879,20 @@ main (int argc, char **argv) { static unsigned char apdu[] = {0, 0x20, 0, 0x81, 6, '1','2','3','4','5','6'}; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } } - if (!rc) + if (!err) { fputs ("getting OpenPGP DO 0x5E ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } } @@ -3884,7 +3903,7 @@ main (int argc, char **argv) /* * Local Variables: - * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c" + * compile-command: "gcc -DTEST -DGPGRT_ENABLE_ES_MACROS -DHAVE_NPTH -DUSE_NPTH -Wall -I/usr/include/libusb-1.0 -I/usr/local/include -lusb-1.0 -g ccid-driver.c -lnpth -lgpg-error" * End: */ #endif /*TEST*/ diff --git a/scd/command.c b/scd/command.c index 0d1a5cd3f..0096ca96d 100644 --- a/scd/command.c +++ b/scd/command.c @@ -33,7 +33,6 @@ #include "scdaemon.h" #include <assuan.h> #include <ksba.h> -#include "app-common.h" #include "iso7816.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "atr.h" @@ -145,10 +144,10 @@ hex_to_buffer (const char *string, size_t *r_length) static void do_reset (ctrl_t ctrl, int send_reset) { - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; - if (app) - app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset); + if (card) + card_reset (card, ctrl, IS_LOCKED (ctrl)? 0: send_reset); /* If we hold a lock, unlock now. */ if (locked_session && ctrl->server_local == locked_session) @@ -157,6 +156,8 @@ do_reset (ctrl_t ctrl, int send_reset) log_info ("implicitly unlocking due to RESET\n"); } } + + static gpg_error_t reset_notify (assuan_context_t ctx, char *line) @@ -211,38 +212,50 @@ open_card (ctrl_t ctrl) if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); - if (ctrl->app_ctx) + if (ctrl->card_ctx) return 0; - return select_application (ctrl, NULL, &ctrl->app_ctx, 0, NULL, 0); + return select_application (ctrl, NULL, &ctrl->card_ctx, 0, NULL, 0); } /* Explicitly open a card for a specific use of APPTYPE or SERIALNO. */ static gpg_error_t -open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno) +open_card_with_request (ctrl_t ctrl, + const char *apptypestr, const char *serialno) { gpg_error_t err; unsigned char *serialno_bin = NULL; size_t serialno_bin_len = 0; - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; + + if (serialno) + serialno_bin = hex_to_buffer (serialno, &serialno_bin_len); /* If we are already initialized for one specific application we need to check that the client didn't requested a specific application different from the one in use before we continue. */ - if (apptype && ctrl->app_ctx) - return check_application_conflict (apptype, ctrl->app_ctx); - - /* Re-scan USB devices. Release APP, before the scan. */ - ctrl->app_ctx = NULL; - release_application (app, 0); + if (apptypestr && ctrl->card_ctx) + { + err = check_application_conflict (ctrl->card_ctx, apptypestr, + serialno_bin, serialno_bin_len); + if (gpg_err_code (err) == GPG_ERR_FALSE) + { + /* Different application but switching is supported. */ + err = select_additional_application (ctrl, apptypestr); + } + goto leave; + } - if (serialno) - serialno_bin = hex_to_buffer (serialno, &serialno_bin_len); + /* Re-scan USB devices. Release CARD, before the scan. */ + /* FIXME: Is a card_unref sufficient or do we need to deallocate? */ + ctrl->card_ctx = NULL; + card_unref (card); - err = select_application (ctrl, apptype, &ctrl->app_ctx, 1, + err = select_application (ctrl, apptypestr, &ctrl->card_ctx, 1, serialno_bin, serialno_bin_len); - xfree (serialno_bin); + leave: + xfree (serialno_bin); return err; } @@ -313,7 +326,7 @@ cmd_serialno (assuan_context_t ctx, char *line) c->server_local->card_removed = 0; } - serial = app_get_serialno (ctrl->app_ctx); + serial = card_get_serialno (ctrl->card_ctx); if (!serial) return gpg_error (GPG_ERR_INV_VALUE); @@ -411,20 +424,20 @@ cmd_learn (assuan_context_t ctx, char *line) { const char *reader; char *serial; - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; - if (!app) + if (!card) return gpg_error (GPG_ERR_CARD_NOT_PRESENT); - reader = apdu_get_reader_name (app->slot); + reader = apdu_get_reader_name (card->slot); if (!reader) return out_of_core (); send_status_direct (ctrl, "READER", reader); /* No need to free the string of READER. */ - serial = app_get_serialno (ctrl->app_ctx); + serial = card_get_serialno (ctrl->card_ctx); if (!serial) - return gpg_error (GPG_ERR_INV_VALUE); + return gpg_error (GPG_ERR_INV_VALUE); rc = assuan_write_status (ctx, "SERIALNO", serial); if (rc < 0) @@ -461,7 +474,7 @@ cmd_learn (assuan_context_t ctx, char *line) /* Let the application print out its collection of useful status information. */ if (!rc) - rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo); + rc = app_write_learn_status (ctrl->card_ctx, ctrl, only_keypairinfo); return rc; } @@ -484,7 +497,7 @@ cmd_readcert (assuan_context_t ctx, char *line) return rc; line = xstrdup (line); /* Need a copy of the line. */ - rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert); + rc = app_readcert (ctrl->card_ctx, ctrl, line, &cert, &ncert); if (rc) log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); xfree (line); @@ -502,19 +515,20 @@ cmd_readcert (assuan_context_t ctx, char *line) static const char hlp_readkey[] = - "READKEY [--advanced] <keyid>|<oid>\n" + "READKEY [--advanced] [--info[-only]] <keyid>|<oid>\n" "\n" "Return the public key for the given cert or key ID as a standard\n" - "S-expression.\n" - "In --advanced mode it returns the S-expression in advanced format.\n" - "\n" - "Note that this function may even be used on a locked card."; + "S-expression. With --advanced the S-expression is returned in\n" + "advanced format. With --info a KEYPAIRINFO status line is also\n" + "emitted; with --info-only the regular output is suppressed."; static gpg_error_t cmd_readkey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; int advanced = 0; + int opt_info = 0; + int opt_nokey = 0; unsigned char *cert = NULL; unsigned char *pk = NULL; size_t ncert, pklen; @@ -524,6 +538,10 @@ cmd_readkey (assuan_context_t ctx, char *line) if (has_option (line, "--advanced")) advanced = 1; + if (has_option (line, "--info")) + opt_info = 1; + if (has_option (line, "--info-only")) + opt_info = opt_nokey = 1; line = skip_options (line); line = xstrdup (line); /* Need a copy of the line. */ @@ -531,14 +549,16 @@ cmd_readkey (assuan_context_t ctx, char *line) /* If the application supports the READKEY function we use that. Otherwise we use the old way by extracting it from the certificate. */ - rc = app_readkey (ctrl->app_ctx, ctrl, line, &pk, &pklen); + rc = app_readkey (ctrl->card_ctx, ctrl, line, + opt_info? APP_READKEY_FLAG_INFO : 0, + opt_nokey? NULL : &pk, &pklen); if (!rc) ; /* Okay, got that key. */ else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { /* Fall back to certificate reading. */ - rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert); + rc = app_readcert (ctrl->card_ctx, ctrl, line, &cert, &ncert); if (rc) { log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); @@ -551,6 +571,26 @@ cmd_readkey (assuan_context_t ctx, char *line) gpg_strerror (rc)); goto leave; } + + if (opt_info) + { + char keygripstr[KEYGRIP_LEN*2+1]; + + rc = app_help_get_keygrip_string_pk (pk, pklen, keygripstr); + if (rc) + { + log_error ("app_help_get_keygrip_string failed: %s\n", + gpg_strerror (rc)); + goto leave; + } + + /* FIXME: Using LINE is not correct because it might be an + * OID and has not been canonicalized (i.e. uppercased). */ + send_status_info (ctrl, "KEYPAIRINFO", + keygripstr, strlen (keygripstr), + line, strlen (line), + NULL, (size_t)0); + } } else { @@ -558,7 +598,9 @@ cmd_readkey (assuan_context_t ctx, char *line) goto leave; } - if (advanced) + if (opt_nokey) + ; + else if (advanced) { gcry_sexp_t s_key; unsigned char *pkadv; @@ -728,6 +770,8 @@ cmd_pksign (assuan_context_t ctx, char *line) size_t outdatalen; char *keyidstr; int hash_algo; + card_t card; + int direct = 0; if (has_option (line, "--hash=rmd160")) hash_algo = GCRY_MD_RMD160; @@ -760,11 +804,30 @@ cmd_pksign (assuan_context_t ctx, char *line) if (!keyidstr) return out_of_core (); - rc = app_sign (ctrl->app_ctx, ctrl, - keyidstr, hash_algo, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + /* When it's a keygrip, we directly use the card, with no change of + ctrl->card_ctx. */ + if (strlen (keyidstr) == 40) + { + card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); + direct = 1; + } + else + card = ctrl->card_ctx; + + if (card) + { + if (direct) + card_ref (card); + rc = app_sign (card, ctrl, + keyidstr, hash_algo, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + if (direct) + card_unref (card); + } + else + rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) @@ -793,11 +856,13 @@ cmd_pkauth (assuan_context_t ctx, char *line) unsigned char *outdata; size_t outdatalen; char *keyidstr; + card_t card; + int direct = 0; if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We have to use a copy of the key ID because the function may use @@ -807,9 +872,29 @@ cmd_pkauth (assuan_context_t ctx, char *line) if (!keyidstr) return out_of_core (); - rc = app_auth (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + /* When it's a keygrip, we directly use CARD, with no change of + ctrl->card_ctx. */ + if (strlen (keyidstr) == 40) + { + card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); + direct = 1; + } + else + card = ctrl->card_ctx; + + if (card) + { + if (direct) + card_ref (card); + rc = app_auth (card, ctrl, keyidstr, pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + if (direct) + card_unref (card); + } + else + rc = gpg_error (GPG_ERR_NO_SECKEY); + xfree (keyidstr); if (rc) { @@ -838,6 +923,8 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) size_t outdatalen; char *keyidstr; unsigned int infoflags; + card_t card; + int direct = 0; if ((rc = open_card (ctrl))) return rc; @@ -845,9 +932,29 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) keyidstr = xtrystrdup (line); if (!keyidstr) return out_of_core (); - rc = app_decipher (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen, &infoflags); + + /* When it's a keygrip, we directly use CARD, with no change of + ctrl->card_ctx. */ + if (strlen (keyidstr) == 40) + { + card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); + direct = 1; + } + else + card = ctrl->card_ctx; + + if (card) + { + if (direct) + card_ref (card); + rc = app_decipher (card, ctrl, keyidstr, pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen, &infoflags); + if (direct) + card_unref (card); + } + else + rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) @@ -905,7 +1012,7 @@ cmd_getattr (assuan_context_t ctx, char *line) /* FIXME: Applications should not return sensitive data if the card is locked. */ - rc = app_getattr (ctrl->app_ctx, ctrl, keyword); + rc = app_getattr (ctrl->card_ctx, ctrl, keyword); return rc; } @@ -967,7 +1074,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) assuan_end_confidential (ctx); if (!err) { - err = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx, + err = app_setattr (ctrl->card_ctx, ctrl, keyword, pin_cb, ctx, value, nbytes); wipememory (value, nbytes); xfree (value); @@ -977,7 +1084,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) else { nbytes = percent_plus_unescape_inplace (line, 0); - err = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx, + err = app_setattr (ctrl->card_ctx, ctrl, keyword, pin_cb, ctx, (const unsigned char*)line, nbytes); } @@ -1018,7 +1125,7 @@ cmd_writecert (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); certid = xtrystrdup (certid); @@ -1035,7 +1142,7 @@ cmd_writecert (assuan_context_t ctx, char *line) } /* Write the certificate to the card. */ - rc = app_writecert (ctrl->app_ctx, ctrl, certid, + rc = app_writecert (ctrl->card_ctx, ctrl, certid, pin_cb, ctx, certdata, certdatalen); xfree (certid); xfree (certdata); @@ -1080,7 +1187,7 @@ cmd_writekey (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); keyid = xtrystrdup (keyid); @@ -1098,7 +1205,7 @@ cmd_writekey (assuan_context_t ctx, char *line) } /* Write the key to the card. */ - rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0, + rc = app_writekey (ctrl->card_ctx, ctrl, keyid, force? 1:0, pin_cb, ctx, keydata, keydatalen); xfree (keyid); xfree (keydata); @@ -1172,7 +1279,7 @@ cmd_genkey (assuan_context_t ctx, char *line) if ((err = open_card (ctrl))) goto leave; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); keyref = keyref_buffer = xtrystrdup (keyref); @@ -1181,7 +1288,7 @@ cmd_genkey (assuan_context_t ctx, char *line) err = gpg_error_from_syserror (); goto leave; } - err = app_genkey (ctrl->app_ctx, ctrl, keyref, opt_algo, + err = app_genkey (ctrl->card_ctx, ctrl, keyref, opt_algo, force? APP_GENKEY_FLAG_FORCE : 0, timestamp, pin_cb, ctx); @@ -1216,14 +1323,14 @@ cmd_random (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); buffer = xtrymalloc (nbytes); if (!buffer) return out_of_core (); - rc = app_get_challenge (ctrl->app_ctx, ctrl, nbytes, buffer); + rc = app_get_challenge (ctrl->card_ctx, ctrl, nbytes, buffer); if (!rc) { rc = assuan_send_data (ctx, buffer, nbytes); @@ -1277,13 +1384,13 @@ cmd_passwd (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); chvnostr = xtrystrdup (chvnostr); if (!chvnostr) return out_of_core (); - rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, flags, pin_cb, ctx); + rc = app_change_pin (ctrl->card_ctx, ctrl, chvnostr, flags, pin_cb, ctx); if (rc) log_error ("command passwd failed: %s\n", gpg_strerror (rc)); xfree (chvnostr); @@ -1334,7 +1441,7 @@ cmd_checkpin (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We have to use a copy of the key ID because the function may use @@ -1344,7 +1451,7 @@ cmd_checkpin (assuan_context_t ctx, char *line) if (!idstr) return out_of_core (); - rc = app_check_pin (ctrl->app_ctx, ctrl, idstr, pin_cb, ctx); + rc = app_check_pin (ctrl->card_ctx, ctrl, idstr, pin_cb, ctx); xfree (idstr); if (rc) log_error ("app_check_pin failed: %s\n", gpg_strerror (rc)); @@ -1525,7 +1632,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - app_send_card_list (ctrl); + rc = app_send_card_list (ctrl); } else rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); @@ -1547,14 +1654,14 @@ static gpg_error_t cmd_restart (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; (void)line; - if (app) + if (card) { - ctrl->app_ctx = NULL; - release_application (app, 0); + ctrl->card_ctx = NULL; + card_unref (card); } if (locked_session && ctrl->server_local == locked_session) { @@ -1576,10 +1683,10 @@ cmd_disconnect (assuan_context_t ctx, char *line) (void)line; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - apdu_disconnect (ctrl->app_ctx->slot); + apdu_disconnect (ctrl->card_ctx->slot); return 0; } @@ -1608,7 +1715,7 @@ static gpg_error_t cmd_apdu (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - app_t app; + card_t card; int rc; unsigned char *apdu; size_t apdulen; @@ -1638,8 +1745,8 @@ cmd_apdu (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - app = ctrl->app_ctx; - if (!app) + card = ctrl->card_ctx; + if (!card) return gpg_error (GPG_ERR_CARD_NOT_PRESENT); if (with_atr) @@ -1648,7 +1755,7 @@ cmd_apdu (assuan_context_t ctx, char *line) size_t atrlen; char hexbuf[400]; - atr = apdu_get_atr (app->slot, &atrlen); + atr = apdu_get_atr (card->slot, &atrlen); if (!atr || atrlen > sizeof hexbuf - 2 ) { rc = gpg_error (GPG_ERR_INV_CARD); @@ -1693,7 +1800,7 @@ cmd_apdu (assuan_context_t ctx, char *line) unsigned char *result = NULL; size_t resultlen; - rc = apdu_send_direct (app->slot, exlen, + rc = apdu_send_direct (card->slot, exlen, apdu, apdulen, handle_more, NULL, &result, &resultlen); if (rc) @@ -1728,6 +1835,87 @@ cmd_killscd (assuan_context_t ctx, char *line) } +static const char hlp_keyinfo[] = + "KEYINFO [--list] [--data] <keygrip>\n" + "\n" + "Return information about the key specified by the KEYGRIP. If the\n" + "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n" + "--list is given the keygrip is ignored and information about all\n" + "available keys are returned. Unless --data is given, the\n" + "information is returned as a status line using the format:\n" + "\n" + " KEYINFO <keygrip> T <serialno> <idstr>\n" + "\n" + "KEYGRIP is the keygrip.\n" + "\n" + "SERIALNO is an ASCII string with the serial number of the\n" + " smartcard. If the serial number is not known a single\n" + " dash '-' is used instead.\n" + "\n" + "IDSTR is the IDSTR used to distinguish keys on a smartcard. If it\n" + " is not known a dash is used instead.\n" + "\n" + "More information may be added in the future."; +static gpg_error_t +cmd_keyinfo (assuan_context_t ctx, char *line) +{ + int list_mode; + int opt_data; + int action; + char *keygrip_str; + ctrl_t ctrl = assuan_get_pointer (ctx); + card_t card; + + list_mode = has_option (line, "--list"); + opt_data = has_option (line, "--data"); + line = skip_options (line); + + if (list_mode) + keygrip_str = NULL; + else + keygrip_str = line; + + if (opt_data) + action = KEYGRIP_ACTION_SEND_DATA; + else + action = KEYGRIP_ACTION_WRITE_STATUS; + + card = app_do_with_keygrip (ctrl, action, keygrip_str); + + if (!list_mode && !card) + return gpg_error (GPG_ERR_NOT_FOUND); + return 0; +} + + +/* Send a keyinfo string as used by the KEYGRIP_ACTION_SEND_DATA. If + * DATA is true the string is emitted as a data line, else as a status + * line. */ +void +send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, + const char *serialno, const char *idstr) +{ + char *string; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + + string = xtryasprintf ("%s T %s %s%s", keygrip_str, + serialno? serialno : "-", + idstr? idstr : "-", + data? "\n" : ""); + + if (!string) + return; + + if (!data) + assuan_write_status (ctx, "KEYINFO", string); + else + assuan_send_data (ctx, string, strlen (string)); + + xfree (string); + return; +} + + /* Tell the assuan library about our commands */ static int @@ -1763,6 +1951,7 @@ register_commands (assuan_context_t ctx) { "DISCONNECT", cmd_disconnect,hlp_disconnect }, { "APDU", cmd_apdu, hlp_apdu }, { "KILLSCD", cmd_killscd, hlp_killscd }, + { "KEYINFO", cmd_keyinfo, hlp_keyinfo }, { NULL } }; int i, rc; @@ -1890,6 +2079,20 @@ scd_command_handler (ctrl_t ctrl, int fd) } +/* Clear the current application info for CARD from all sessions. + * This is used while deallocating a card. */ +void +scd_clear_current_app (card_t card) +{ + struct server_local_s *sl; + + for (sl=session_list; sl; sl = sl->next_session) + { + if (sl->ctrl_backlink->card_ctx == card) + sl->ctrl_backlink->current_apptype = APPTYPE_NONE; + } +} + /* Send a line with status information via assuan and escape all given buffers. The variable elements are pairs of (char *, size_t), terminated with a (NULL, 0). */ @@ -2001,9 +2204,10 @@ popup_prompt (void *opaque, int on) } -/* Helper to send the clients a status change notification. */ +/* Helper to send the clients a status change notification. Note that + * this function assumes that APP is already locked. */ void -send_client_notifications (app_t app, int removal) +send_client_notifications (card_t card, int removal) { struct { pid_t pid; @@ -2018,7 +2222,7 @@ send_client_notifications (app_t app, int removal) struct server_local_s *sl; for (sl=session_list; sl; sl = sl->next_session) - if (sl->ctrl_backlink && sl->ctrl_backlink->app_ctx == app) + if (sl->ctrl_backlink && sl->ctrl_backlink->card_ctx == card) { pid_t pid; #ifdef HAVE_W32_SYSTEM @@ -2029,9 +2233,9 @@ send_client_notifications (app_t app, int removal) if (removal) { - sl->ctrl_backlink->app_ctx = NULL; + sl->ctrl_backlink->card_ctx = NULL; sl->card_removed = 1; - release_application (app, 1); + card_unref_locked (card); } if (!sl->event_signal || !sl->assuan_ctx) diff --git a/scd/iso7816.c b/scd/iso7816.c index d9f3336c7..954aa3d4a 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -23,20 +23,9 @@ #include <stdlib.h> #include <string.h> -#if defined(GNUPG_SCD_MAIN_HEADER) -#include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 -/* This is used with GnuPG version < 1.9. The code has been source - copied from the current GnuPG >= 1.9 and is maintained over - there. */ -#include "options.h" -#include "errors.h" -#include "memory.h" -#include "../common/util.h" -#include "../common/i18n.h" -#else /* GNUPG_MAJOR_VERSION != 1 */ -#include "scdaemon.h" -#endif /* GNUPG_MAJOR_VERSION != 1 */ +#if defined(GNUPG_MAJOR_VERSION) +# include "scdaemon.h" +#endif /*GNUPG_MAJOR_VERSION*/ #include "iso7816.h" #include "apdu.h" diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 507108db0..e89569e5d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -46,7 +46,6 @@ #include "../common/i18n.h" #include "../common/sysutils.h" -#include "app-common.h" #include "iso7816.h" #include "apdu.h" #include "ccid-driver.h" @@ -98,6 +97,7 @@ enum cmd_and_opt_values oAllowAdmin, oDenyAdmin, oDisableApplication, + oApplicationPriority, oEnablePinpadVarlen, oListenBacklog }; @@ -154,6 +154,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oDenyAdmin, "deny-admin", N_("deny the use of admin card commands")), ARGPARSE_s_s (oDisableApplication, "disable-application", "@"), + ARGPARSE_s_s (oApplicationPriority, "application-priority", + N_("|LIST|Change the application priority to LIST")), ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen", N_("use variable length input for pinpad")), ARGPARSE_s_s (oHomedir, "homedir", "@"), @@ -436,6 +438,7 @@ main (int argc, char **argv ) struct assuan_malloc_hooks malloc_hooks; int res; npth_t pipecon_handler; + const char *application_priority = NULL; early_system_init (); set_strusage (my_strusage); @@ -616,6 +619,10 @@ main (int argc, char **argv ) add_to_strlist (&opt.disabled_applications, pargs.r.ret_str); break; + case oApplicationPriority: + application_priority = pargs.r.ret_str; + break; + case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break; case oListenBacklog: @@ -720,6 +727,7 @@ main (int argc, char **argv ) es_printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE ); es_printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0); es_printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE ); + es_printf ("application-priority:%lu:\n", GC_OPT_FLAG_NONE ); scd_exit (0); } @@ -739,6 +747,9 @@ main (int argc, char **argv ) log_debug ("... okay\n"); } + if (application_priority) + app_update_priority_list (application_priority); + if (pipe_server) { /* This is the simple pipe based server */ diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 73589ade8..b709b1cbc 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -30,6 +30,8 @@ #include <gcrypt.h> #include "../common/util.h" #include "../common/sysutils.h" +#include "app-common.h" + /* To convey some special hash algorithms we use algorithm numbers reserved for application use. */ @@ -84,6 +86,7 @@ struct #define DBG_READER (opt.debug & DBG_READER_VALUE) struct server_local_s; +struct card_ctx_s; struct app_ctx_s; struct server_control_s @@ -101,7 +104,12 @@ struct server_control_s associated. Note that this is shared with the other connections: All connections accessing the same reader are using the same application context. */ - struct app_ctx_s *app_ctx; + struct card_ctx_s *card_ctx; + + /* The currently active application for this context. We need to + * know this for cards which are able to switch on the fly between + * apps. */ + apptype_t current_apptype; /* Helper to store the value we are going to sign */ struct @@ -111,7 +119,6 @@ struct server_control_s } in_data; }; -typedef struct app_ctx_s *app_t; /*-- scdaemon.c --*/ void scd_exit (int rc); @@ -120,14 +127,20 @@ const char *scd_get_socket_name (void); /*-- command.c --*/ gpg_error_t initialize_module_command (void); int scd_command_handler (ctrl_t, int); +void scd_clear_current_app (card_t card); void send_status_info (ctrl_t ctrl, const char *keyword, ...) GPGRT_ATTR_SENTINEL(1); void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args); gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...) GPGRT_ATTR_PRINTF(3,4); +void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, + const char *serialno, const char *idstr); void popup_prompt (void *opaque, int on); -void send_client_notifications (app_t app, int removal); + +/* Take care: this function assumes that CARD is locked. */ +void send_client_notifications (card_t card, int removal); + void scd_kick_the_loop (void); int get_active_connection_count (void); |