diff --git a/g10/ChangeLog b/g10/ChangeLog index aa5843a64..8f05b7e5b 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,21 @@ +2005-05-20 Werner Koch + + * ccid-driver.c (ccid_transceive): Arghhh. The seqno is another + bit in the R-block than in the I block, this was wrong at one + place. Fixes bug #419 and hopefully several others. + +2005-05-19 Werner Koch + + * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer + version in gnupg 1.9 CVS. + +2005-05-18 Werner Koch + + * passphrase.c (agent_open): Made global and add arg TRY. + (agent_close): Made global. + + * app-common.h (app_t): Add a field to store the Assuan context. + 2005-05-13 David Shaw * build-packet.c (do_comment): Removed. diff --git a/g10/app-common.h b/g10/app-common.h index 48bd349f4..f1058dda1 100644 --- a/g10/app-common.h +++ b/g10/app-common.h @@ -1,5 +1,5 @@ /* app-common.h - Common declarations for all card applications - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,10 +23,15 @@ #ifndef GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H -#if GNUPG_MAJOR_VERSION != 1 -#include +#if GNUPG_MAJOR_VERSION == 1 +# ifdef ENABLE_AGENT_SUPPORT +# include "assuan.h" +# endif +#else +# include #endif + struct app_local_s; /* Defined by all app-*.c. */ struct app_ctx_s { @@ -35,6 +40,15 @@ struct app_ctx_s { unsupported operations the particular function pointer is set to NULL */ int slot; /* Used reader. */ + + /* If this is used by GnuPG 1.4 we need to know the assuan context + in case we need to divert the operation to an already running + agent. This if ASSUAN_CTX is not NULL we take this as indication + that all operations are diverted to gpg-agent. */ +#if GNUPG_MAJOR_VERSION == 1 && defined(ENABLE_AGENT_SUPPORT) + assuan_context_t assuan_ctx; +#endif /*GNUPG_MAJOR_VERSION == 1*/ + unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ const char *apptype; @@ -46,54 +60,56 @@ struct app_ctx_s { struct app_local_s *app_local; /* Local to the application. */ struct { void (*deinit) (app_t app); - int (*learn_status) (app_t app, ctrl_t ctrl); - int (*readcert) (app_t app, const char *certid, + gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl); + gpg_error_t (*readcert) (app_t app, const char *certid, unsigned char **cert, size_t *certlen); - int (*getattr) (app_t app, ctrl_t ctrl, const char *name); - int (*setattr) (app_t app, const char *name, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*readkey) (app_t app, const char *certid, + 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 **), void *pincb_arg, const unsigned char *value, size_t valuelen); - int (*sign) (app_t app, + gpg_error_t (*sign) (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); - int (*auth) (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*auth) (app_t app, 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); - int (*decipher) (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*decipher) (app_t app, 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); - int (*genkey) (app_t app, ctrl_t ctrl, + gpg_error_t (*genkey) (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*change_pin) (app_t app, ctrl_t ctrl, + gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*check_pin) (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*check_pin) (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); } fnc; }; #if GNUPG_MAJOR_VERSION == 1 -int app_select_openpgp (app_t app); -int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); -int app_openpgp_storekey (app_t app, int keyno, +gpg_error_t app_select_openpgp (app_t app); +gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); +gpg_error_t app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); #else /*-- app-help.c --*/ @@ -102,75 +118,79 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ -app_t select_application (ctrl_t ctrl, int slot, const char *name); +gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, + app_t *r_app); void release_application (app_t app); -int app_munge_serialno (app_t app); -int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); -int app_write_learn_status (app_t app, ctrl_t ctrl); -int app_readcert (app_t app, const char *certid, +gpg_error_t app_munge_serialno (app_t app); +gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); +gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl); +gpg_error_t app_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen); -int app_getattr (app_t app, ctrl_t ctrl, const char *name); -int app_setattr (app_t app, const char *name, - int (*pincb)(void*, const char *, char **), +gpg_error_t app_readkey (app_t app, 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, const char *name, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); -int app_sign (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), +gpg_error_t app_sign (app_t app, 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 ); -int app_auth (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), +gpg_error_t app_auth (app_t app, 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); -int app_decipher (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), +gpg_error_t app_decipher (app_t app, 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 ); -int app_genkey (app_t app, ctrl_t ctrl, +gpg_error_t app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer); -int app_change_pin (app_t app, ctrl_t ctrl, +gpg_error_t app_get_challenge (app_t app, size_t nbytes, + unsigned char *buffer); +gpg_error_t app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_check_pin (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), +gpg_error_t app_check_pin (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); /*-- app-openpgp.c --*/ -int app_select_openpgp (app_t app); +gpg_error_t app_select_openpgp (app_t app); -int app_openpgp_cardinfo (app_t app, +gpg_error_t app_openpgp_cardinfo (app_t app, char **serialno, char **disp_name, char **pubkey_url, unsigned char **fpr1, unsigned char **fpr2, unsigned char **fpr3); -int app_openpgp_storekey (app_t app, int keyno, +gpg_error_t app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_openpgp_readkey (app_t app, int keyno, +gpg_error_t app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen, unsigned char **e, size_t *elen); /*-- app-nks.c --*/ -int app_select_nks (app_t app); +gpg_error_t app_select_nks (app_t app); /*-- app-dinsig.c --*/ -int app_select_dinsig (app_t app); +gpg_error_t app_select_dinsig (app_t app); /*-- app-p15.c --*/ -int app_select_p15 (app_t app); +gpg_error_t app_select_p15 (app_t app); #endif diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c index 27fc0585c..6f4778ba5 100644 --- a/g10/app-openpgp.c +++ b/g10/app-openpgp.c @@ -90,6 +90,7 @@ static struct { }; +/* One cache item for DOs. */ struct cache_s { struct cache_s *next; int tag; @@ -97,8 +98,27 @@ struct cache_s { unsigned char data[1]; }; + +/* Object with application (i.e. OpenPGP card) specific data. */ struct app_local_s { + /* A linked list with cached DOs. */ struct cache_s *cache; + + /* Keep track of the public keys. */ + struct + { + int read_done; /* True if we have at least tried to read them. */ + unsigned char *key; /* This is a malloced buffer with a canonical + encoded S-expression encoding a public + key. Might be NULL if key is not + available. */ + size_t keylen; /* The length of the above S-expression. Thsi + is usullay only required for corss checks + because the length of an S-expression is + implicitly available. */ + } pk[3]; + + /* Keep track of card capabilities. */ struct { unsigned int get_challenge:1; @@ -106,6 +126,8 @@ struct app_local_s { unsigned int change_force_chv:1; unsigned int private_dos:1; } extcap; + + /* Flags used to control the application. */ struct { unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */ @@ -114,10 +136,16 @@ struct app_local_s { }; + +/***** Local prototypes *****/ static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen); -static unsigned long get_sig_counter (APP app); +static unsigned long get_sig_counter (app_t app); + + + + /* Deconstructor. */ static void do_deinit (app_t app) @@ -125,12 +153,19 @@ do_deinit (app_t app) if (app && app->app_local) { struct cache_s *c, *c2; + int i; for (c = app->app_local->cache; c; c = c2) { c2 = c->next; xfree (c); } + + for (i=0; i < DIM (app->app_local->pk); i++) + { + xfree (app->app_local->pk[i].key); + app->app_local->pk[i].read_done = 0; + } xfree (app->app_local); app->app_local = NULL; } @@ -278,23 +313,33 @@ flush_cache (app_t app) NULL if not found or a pointer which must be used to release the buffer holding value. */ static void * -get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) +get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, + int *r_rc) { int rc, i; unsigned char *buffer; size_t buflen; unsigned char *value; size_t valuelen; + int dummyrc; + + if (!r_rc) + r_rc = &dummyrc; *result = NULL; *nbytes = 0; + *r_rc = 0; for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) ; if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11) { - if( iso7816_get_data (app->slot, tag, &buffer, &buflen)) - return NULL; + rc = iso7816_get_data (app->slot, tag, &buffer, &buflen); + if (rc) + { + *r_rc = rc; + return NULL; + } *result = buffer; *nbytes = buflen; return buffer; @@ -306,12 +351,13 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) { rc = get_cached_data (app, data_objects[i].get_from, &buffer, &buflen, - data_objects[i].get_immediate_in_v11); + (data_objects[i].dont_cache + || data_objects[i].get_immediate_in_v11)); if (!rc) { const unsigned char *s; - s = find_tlv (buffer, buflen, tag, &valuelen); + s = find_tlv_unchecked (buffer, buflen, tag, &valuelen); if (!s) value = NULL; /* not found */ else if (valuelen > buflen - (s - buffer)) @@ -328,7 +374,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) if (!value) /* Not in a constructed DO, try simple. */ { rc = get_cached_data (app, tag, &buffer, &buflen, - data_objects[i].get_immediate_in_v11); + (data_objects[i].dont_cache + || data_objects[i].get_immediate_in_v11)); if (!rc) { value = buffer; @@ -342,6 +389,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) *result = value; return buffer; } + *r_rc = rc; return NULL; } @@ -385,8 +433,8 @@ dump_all_do (int slot) if (j==i || data_objects[i].tag != data_objects[j].get_from) continue; - value = find_tlv (buffer, buflen, - data_objects[j].tag, &valuelen); + value = find_tlv_unchecked (buffer, buflen, + data_objects[j].tag, &valuelen); if (!value) ; /* not found */ else if (valuelen > buflen - (value - buffer)) @@ -460,7 +508,7 @@ parse_login_data (app_t app) app->app_local->flags.def_chv2 = 0; /* Read the DO. */ - relptr = get_one_do (app, 0x005E, &buffer, &buflen); + relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL); if (!relptr) return; /* Ooops. */ for (; buflen; buflen--, buffer++) @@ -499,7 +547,7 @@ parse_login_data (app_t app) } /* Note, that FPR must be at least 20 bytes. */ -static int +static gpg_error_t store_fpr (int slot, int keynumber, u32 timestamp, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, @@ -623,7 +671,7 @@ send_key_data (ctrl_t ctrl, const char *name, /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ -static int +static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { @@ -650,7 +698,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "PRIVATE-DO-4", 0x0104 }, { NULL, 0 } }; - int idx, i; + int idx, i, rc; void *relptr; unsigned char *value; size_t valuelen; @@ -695,7 +743,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) return 0; } - relptr = get_one_do (app, table[idx].tag, &value, &valuelen); + relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc); if (relptr) { if (table[idx].special == 1) @@ -732,11 +780,395 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) xfree (relptr); } + return rc; +} + +/* Retrieve the fingerprint from the card inserted in SLOT and write + 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) +{ + gpg_error_t err = 0; + void *relptr; + unsigned char *value; + size_t valuelen; + int i; + + assert (keyno >=0 && keyno <= 2); + + relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL); + if (relptr && valuelen >= 60) + { + for (i = 0; i < 20; i++) + sprintf (fpr + (i * 2), "%02X", value[(keyno*20)+i]); + } + else + err = gpg_error (GPG_ERR_NOT_FOUND); + xfree (relptr); + return err; +} +#endif /*GNUPG_MAJOR_VERSION > 1*/ + + +/* Retrieve the public key material for the RSA key, whose fingerprint + is FPR, from gpg output, which can be read through the stream FP. + The RSA modulus will be stored at the address of M and MLEN, the + 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, + const unsigned char **e, size_t *elen) +{ + gcry_error_t err = 0; + char *line = NULL; /* read_line() buffer. */ + size_t line_size = 0; /* Helper for for read_line. */ + int found_key = 0; /* Helper to find a matching key. */ + unsigned char *m_new = NULL; + unsigned char *e_new = NULL; + size_t m_new_n = 0; + size_t e_new_n = 0; + + /* Loop over all records until we have found the subkey + corresponsing to the fingerprint. Inm general the first record + should be the pub record, but we don't rely on that. Given that + we only need to look at one key, it is sufficient to compare the + keyid so that we don't need to look at "fpr" records. */ + for (;;) + { + char *p; + char *fields[6]; + int nfields; + size_t max_length; + gcry_mpi_t mpi; + int i; + + max_length = 4096; + i = read_line (fp, &line, &line_size, &max_length); + if (!i) + break; /* EOF. */ + if (i < 0) + { + err = gpg_error_from_errno (errno); + goto leave; /* Error. */ + } + if (!max_length) + { + err = gpg_error (GPG_ERR_TRUNCATED); + goto leave; /* Line truncated - we better stop processing. */ + } + + /* Parse the line into fields. */ + for (nfields=0, p=line; p && nfields < DIM (fields); nfields++) + { + fields[nfields] = p; + p = strchr (p, ':'); + if (p) + *(p++) = 0; + } + if (!nfields) + continue; /* No fields at all - skip line. */ + + if (!found_key) + { + if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") ) + && nfields > 4 && !strcmp (fields[4], hexkeyid)) + found_key = 1; + continue; + } + + if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") ) + break; /* Next key - stop. */ + + if ( strcmp (fields[0], "pkd") ) + continue; /* Not a key data record. */ + i = 0; /* Avoid erroneous compiler warning. */ + if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1 + || (!i && m_new) || (i && e_new)) + { + err = gpg_error (GPG_ERR_GENERAL); + goto leave; /* Error: Invalid key data record or not an RSA key. */ + } + + err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL); + if (err) + mpi = NULL; + else if (!i) + err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &m_new, &m_new_n, mpi); + else + err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi); + gcry_mpi_release (mpi); + if (err) + goto leave; + } + + if (m_new && e_new) + { + *m = m_new; + *mlen = m_new_n; + m_new = NULL; + *e = e_new; + *elen = e_new_n; + e_new = NULL; + } + else + err = gpg_error (GPG_ERR_GENERAL); + + leave: + xfree (m_new); + xfree (e_new); + xfree (line); + return err; +} +#endif /*GNUPG_MAJOR_VERSION > 1*/ + + +/* Get the public key for KEYNO and store it as an S-expresion with + the APP handle. On error that field gets cleared. If we already + know about the public key we will just return. Note that this does + not mean a key is available; this is soley indicated by the + presence of the app->app_local->pk[KEYNO-1].key field. + + 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. However, given that we + use the same code in gpg 1.4, we can't use the gcry S-expresion + here but need to open encode it. */ +#if GNUPG_MAJOR_VERSION > 1 +static gpg_error_t +get_public_key (app_t app, int keyno) +{ + gpg_error_t err = 0; + unsigned char *buffer; + const unsigned char *keydata, *m, *e; + size_t buflen, keydatalen, mlen, elen; + unsigned char *mbuf = NULL; + unsigned char *ebuf = NULL; + unsigned char *keybuf = NULL; + unsigned char *keybuf_p; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + /* Already cached? */ + if (app->app_local->pk[keyno].read_done) + return 0; + + xfree (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].key = NULL; + app->app_local->pk[keyno].keylen = 0; + + if (app->card_version > 0x0100) + { + /* We may simply read the public key out of these cards. */ + err = iso7816_read_public_key (app->slot, + keyno == 0? "\xB6" : + keyno == 1? "\xB8" : "\xA4", + 2, + &buffer, &buflen); + if (err) + { + log_error (_("reading public key failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); + if (!keydata) + { + err = gpg_error (GPG_ERR_CARD); + log_error (_("response does not contain the public key data\n")); + goto leave; + } + + m = find_tlv (keydata, keydatalen, 0x0081, &mlen); + if (!m) + { + err = gpg_error (GPG_ERR_CARD); + log_error (_("response does not contain the RSA modulus\n")); + goto leave; + } + + + e = find_tlv (keydata, keydatalen, 0x0082, &elen); + if (!e) + { + err = gpg_error (GPG_ERR_CARD); + log_error (_("response does not contain the RSA public exponent\n")); + goto leave; + } + + /* Prepend numbers with a 0 if needed. */ + if (mlen && (*m & 0x80)) + { + mbuf = xtrymalloc ( mlen + 1); + if (!mbuf) + { + err = gpg_error_from_errno (errno); + goto leave; + } + *mbuf = 0; + memcpy (mbuf+1, m, mlen); + mlen++; + m = mbuf; + } + if (elen && (*e & 0x80)) + { + ebuf = xtrymalloc ( elen + 1); + if (!ebuf) + { + err = gpg_error_from_errno (errno); + goto leave; + } + *ebuf = 0; + memcpy (ebuf+1, e, elen); + elen++; + e = ebuf; + } + + } + else + { + /* Due to a design problem in v1.0 cards we can't get the public + key out of these cards without doing a verify on CHV3. + Clearly that is not an option and thus we try to locate the + key using an external helper. + + The helper we use here is gpg itself, which should know about + the key in any case. */ + + char fpr[41]; + char *hexkeyid; + char *command = NULL; + FILE *fp; + int ret; + + buffer = NULL; /* We don't need buffer. */ + + err = retrieve_fpr_from_card (app, keyno, fpr); + if (err) + { + log_error ("error while retrieving fpr from card: %s\n", + gpg_strerror (err)); + goto leave; + } + hexkeyid = fpr + 24; + + ret = asprintf (&command, + "gpg --list-keys --with-colons --with-key-data '%s'", + fpr); + if (ret < 0) + { + err = gpg_error_from_errno (errno); + goto leave; + } + + fp = popen (command, "r"); + free (command); + if (!fp) + { + err = gpg_error_from_errno (errno); + log_error ("running gpg failed: %s\n", gpg_strerror (err)); + goto leave; + } + + err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen); + fclose (fp); + if (err) + { + log_error ("error while retrieving key material through pipe: %s\n", + gpg_strerror (err)); + goto leave; + } + } + + /* Allocate a buffer to construct the S-expression. */ + /* FIXME: We should provide a generalized S-expression creation + mechanism. */ + keybuf = xtrymalloc (50 + 2*35 + mlen + elen + 1); + if (!keybuf) + { + err = gpg_error_from_errno (errno); + goto leave; + } + + sprintf (keybuf, "(10:public-key(3:rsa(1:n%u:", (unsigned int) mlen); + keybuf_p = keybuf + strlen (keybuf); + memcpy (keybuf_p, m, mlen); + keybuf_p += mlen; + sprintf (keybuf_p, ")(1:e%u:", (unsigned int)elen); + keybuf_p += strlen (keybuf_p); + memcpy (keybuf_p, e, elen); + keybuf_p += elen; + strcpy (keybuf_p, ")))"); + keybuf_p += strlen (keybuf_p); + + app->app_local->pk[keyno].key = keybuf; + app->app_local->pk[keyno].keylen = (keybuf_p - keybuf); + + leave: + /* Set a flag to indicate that we tried to read the key. */ + app->app_local->pk[keyno].read_done = 1; + + xfree (buffer); + xfree (mbuf); + xfree (ebuf); return 0; } +#endif /* GNUPG_MAJOR_VERSION > 1 */ + + + +/* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3]. + This is used by the LEARN command. */ +static gpg_error_t +send_keypair_info (app_t app, ctrl_t ctrl, int keyno) +{ + 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]; + int i; + + err = get_public_key (app, keyno); + if (err) + goto leave; + + assert (keyno >= 1 && keyno <= 3); + if (!app->app_local->pk[keyno-1].key) + goto leave; /* No such key - ignore. */ + + err = keygrip_from_canon_sexp (app->app_local->pk[keyno-1].key, + app->app_local->pk[keyno-1].keylen, + grip); + if (err) + goto leave; + + for (i=0; i < 20; i++) + sprintf (gripstr+i*2, "%02X", grip[i]); + + sprintf (idbuf, "OPENPGP.%d", keyno); + send_status_info (ctrl, "KEYPAIRINFO", + gripstr, 40, + idbuf, strlen (idbuf), + NULL, (size_t)0); + + leave: +#endif /* GNUPG_MAJOR_VERSION > 1 */ + + return err; +} -static int +/* Handle the LEARN command for OpenPGP. */ +static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl) { do_getattr (app, ctrl, "EXTCAP"); @@ -760,16 +1192,57 @@ do_learn_status (app_t app, ctrl_t ctrl) if (app->did_chv3) do_getattr (app, ctrl, "PRIVATE-DO-4"); } - + send_keypair_info (app, ctrl, 1); + send_keypair_info (app, ctrl, 2); + send_keypair_info (app, ctrl, 3); return 0; } +/* Handle the READKEY command for OpenPGP. 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 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) +{ +#if GNUPG_MAJOR_VERSION > 1 + gpg_error_t err; + int keyno; + unsigned char *buf; + + if (!strcmp (keyid, "OPENPGP.1")) + keyno = 1; + else if (!strcmp (keyid, "OPENPGP.2")) + keyno = 2; + else if (!strcmp (keyid, "OPENPGP.3")) + keyno = 3; + else + return gpg_error (GPG_ERR_INV_ID); + + err = get_public_key (app, keyno); + if (err) + return err; + + buf = app->app_local->pk[keyno-1].key; + if (!buf) + return gpg_error (GPG_ERR_NO_PUBKEY); + *pk = buf; + *pklen = app->app_local->pk[keyno-1].keylen;; + return 0; +#else + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + + + /* Verify CHV2 if required. Depending on the configuration of the card CHV1 will also be verified. */ -static int +static gpg_error_t verify_chv2 (app_t app, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; @@ -823,9 +1296,9 @@ verify_chv2 (app_t app, } /* Verify CHV3 if required. */ -static int +static gpg_error_t verify_chv3 (app_t app, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; @@ -845,7 +1318,7 @@ verify_chv3 (app_t app, unsigned char *value; size_t valuelen; - relptr = get_one_do (app, 0x00C4, &value, &valuelen); + relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); if (!relptr || valuelen < 7) { log_error (_("error retrieving CHV status from card\n")); @@ -897,9 +1370,9 @@ verify_chv3 (app_t app, /* Handle the SETATTR operation. All arguments are already basically checked. */ -static int +static gpg_error_t do_setattr (app_t app, const char *name, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { @@ -965,9 +1438,9 @@ do_setattr (app_t app, const char *name, /* Handle the PASSWD command. */ -static int +static gpg_error_t do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; @@ -1056,9 +1529,9 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, /* Handle the GENKEY command. */ -static int +static gpg_error_t do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; @@ -1082,6 +1555,13 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, generation. This _might_ help a card to gather more entropy. */ flush_cache (app); + /* Obviously we need to remove the cached public key. */ + xfree (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].key = NULL; + app->app_local->pk[keyno].keylen = 0; + app->app_local->pk[keyno].read_done = 0; + + /* Check whether a key already exists. */ rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); if (rc) { @@ -1109,11 +1589,12 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, else log_info (_("generating new key\n")); - + + /* Prepare for key generation by verifying the ADmin PIN. */ rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; - + xfree (buffer); buffer = NULL; #if 1 @@ -1206,7 +1687,7 @@ get_sig_counter (app_t app) size_t valuelen; unsigned long ul; - relptr = get_one_do (app, 0x0093, &value, &valuelen); + relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL); if (!relptr) return 0; ul = convert_sig_counter_value (value, valuelen); @@ -1214,7 +1695,7 @@ get_sig_counter (app_t app) return ul; } -static int +static gpg_error_t compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) { const unsigned char *fpr; @@ -1254,7 +1735,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) the key on the card has been replaced but the shadow information known to gpg was not updated. If there is no fingerprint we assume that this is okay. */ -static int +static gpg_error_t check_against_given_fingerprint (app_t app, const char *fpr, int keyno) { unsigned char tmp[20]; @@ -1285,9 +1766,9 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno) GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the serial number does not match). */ -static int +static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -1308,7 +1789,15 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); - if (indatalen != 20) + if (indatalen == 20) + ; + else if (indatalen == (15 + 20) && hashalgo == GCRY_MD_SHA1 + && !memcmp (indata, sha1_prefix, 15)) + ; + else if (indatalen == (15 + 20) && hashalgo == GCRY_MD_RMD160 + && !memcmp (indata, rmd160_prefix, 15)) + ; + else return gpg_error (GPG_ERR_INV_VALUE); /* Check whether an OpenPGP card of any version has been requested. */ @@ -1419,15 +1908,16 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, /* Compute a digital signature using the INTERNAL AUTHENTICATE command on INDATA which is expected to be the raw message digest. For this application the KEYIDSTR consists of the serialnumber and the - fingerprint delimited by a slash. + fingerprint delimited by a slash. Optionally the id OPENPGP.3 may + be given. Note that this fucntion may return the error code GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the serial number does not match). */ -static int +static gpg_error_t do_auth (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -1444,27 +1934,31 @@ do_auth (app_t app, const char *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); - - for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + if (!strcmp (keyidstr, "OPENPGP.3")) ; - if (n != 32) + else if (strlen (keyidstr) < 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; 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); + 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); + } /* If a fingerprint has been specified check it against the one on the card. This is allows for a meaningful error message in case @@ -1484,9 +1978,9 @@ do_auth (app_t app, const char *keyidstr, } -static int +static gpg_error_t do_decipher (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -1545,15 +2039,14 @@ do_decipher (app_t app, const char *keyidstr, cheap check on the PIN: If there is something wrong with the PIN entry system, only the regular CHV will get blocked and not the dangerous CHV3. KEYIDSTR is the usual card's serial number; an - optional fingerprint part will be ignored. + optional fingerprint part will be ignored. There is a special mode if the keyidstr is "[CHV3]" with the "[CHV3]" being a literal string: The Admin Pin is checked if - and only if the retry counter is still at 3. -*/ -static int + and only if the retry counter is still at 3. */ +static gpg_error_t do_check_pin (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { unsigned char tmp_sn[20]; @@ -1588,6 +2081,7 @@ do_check_pin (app_t app, const char *keyidstr, 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 problem and the check above is merely for a graceful failure @@ -1600,7 +2094,7 @@ do_check_pin (app_t app, const char *keyidstr, size_t valuelen; int count; - relptr = get_one_do (app, 0x00C4, &value, &valuelen); + relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); if (!relptr || valuelen < 7) { log_error (_("error retrieving CHV status from card\n")); @@ -1634,7 +2128,7 @@ do_check_pin (app_t app, const char *keyidstr, /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ -int +gpg_error_t app_select_openpgp (app_t app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; @@ -1685,7 +2179,7 @@ app_select_openpgp (app_t app) goto leave; } - relptr = get_one_do (app, 0x00C4, &buffer, &buflen); + relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); if (!relptr) { log_error (_("can't access %s - invalid OpenPGP card?\n"), @@ -1695,7 +2189,7 @@ app_select_openpgp (app_t app) app->force_chv1 = (buflen && *buffer == 0); xfree (relptr); - relptr = get_one_do (app, 0x00C0, &buffer, &buflen); + relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL); if (!relptr) { log_error (_("can't access %s - invalid OpenPGP card?\n"), @@ -1723,7 +2217,7 @@ app_select_openpgp (app_t app) app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; - app->fnc.readcert = NULL; + app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.genkey = do_genkey; @@ -1747,7 +2241,7 @@ leave: LEARN command returns. All parameters return allocated strings or buffers or NULL if the data object is not available. All returned values are sanitized. */ -int +gpg_error_t app_openpgp_cardinfo (app_t app, char **serialno, char **disp_name, @@ -1778,7 +2272,7 @@ app_openpgp_cardinfo (app_t app, if (disp_name) { *disp_name = NULL; - relptr = get_one_do (app, 0x005B, &value, &valuelen); + relptr = get_one_do (app, 0x005B, &value, &valuelen, NULL); if (relptr) { *disp_name = make_printable_string (value, valuelen, 0); @@ -1789,7 +2283,7 @@ app_openpgp_cardinfo (app_t app, if (pubkey_url) { *pubkey_url = NULL; - relptr = get_one_do (app, 0x5F50, &value, &valuelen); + relptr = get_one_do (app, 0x5F50, &value, &valuelen, NULL); if (relptr) { *pubkey_url = make_printable_string (value, valuelen, 0); @@ -1803,7 +2297,7 @@ app_openpgp_cardinfo (app_t app, *fpr2 = NULL; if (fpr3) *fpr3 = NULL; - relptr = get_one_do (app, 0x00C5, &value, &valuelen); + relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL); if (relptr && valuelen >= 60) { if (fpr1) @@ -1837,13 +2331,13 @@ app_openpgp_cardinfo (app_t app, create the fingerprint. M, MLEN is the RSA modulus and E, ELEN the RSA public exponent. This function silently overwrites an existing key.*/ -int +gpg_error_t app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; @@ -1859,6 +2353,11 @@ app_openpgp_storekey (app_t app, int keyno, flush_cache (app); + xfree (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].key = NULL; + app->app_local->pk[keyno].keylen = 0; + app->app_local->pk[keyno].read_done = 0; + rc = iso7816_put_data (app->slot, (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, template, template_len); @@ -1882,7 +2381,7 @@ app_openpgp_storekey (app_t app, int keyno, /* Utility function for external tools: Read the public RSA key at KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */ -int +gpg_error_t app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen, unsigned char **e, size_t *elen) { diff --git a/g10/cardglue.c b/g10/cardglue.c index 9e7aef81f..940ec64a3 100644 --- a/g10/cardglue.c +++ b/g10/cardglue.c @@ -43,9 +43,16 @@ #include "apdu.h" #include "app-common.h" +/* If we build w/o agent support, assuan.h won't be included and thus + we need to define a repalcement for the assuan error type. */ +#ifndef ENABLE_AGENT_SUPPORT +typedef int assuan_error_t; +#endif + + struct ctrl_ctx_s { - int (*status_cb)(void *opaque, const char *line); + assuan_error_t (*status_cb)(void *opaque, const char *line); void *status_cb_arg; }; @@ -57,10 +64,14 @@ struct pincb_parm_s static char *default_reader_port; -static APP current_app; +static app_t current_app; +/* Local prototypes. */ +static assuan_error_t learn_status_cb (void *opaque, const char *line); + + /* Create a serialno/fpr string from the serial number and the secret key. caller must free the returned string. There is no error return. [Taken from 1.9's keyid.c]*/ @@ -135,8 +146,12 @@ send_status_info (CTRL ctrl, const char *keyword, ...) } -void gcry_md_hash_buffer (int algo, void *digest, - const void *buffer, size_t length) +/* Replacement function of the Libgcrypt onewhich is used in gnupg + 1.9. Thus function computes the digest of ALGO from the data in + BUFFER of LENGTH. ALGO must be supported. */ +void +gcry_md_hash_buffer (int algo, void *digest, + const void *buffer, size_t length) { MD_HANDLE h = md_open (algo, 0); if (!h) @@ -204,7 +219,7 @@ card_set_reader_port (const char *portstr) no update time is available the returned value is 0. Caller must free SERIAL unless the function returns an error. */ int -app_get_serial_and_stamp (APP app, char **serial, time_t *stamp) +app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp) { unsigned char *buf, *p; int i; @@ -254,19 +269,91 @@ agent_release_card_info (struct agent_card_info_s *info) } +/* Print an error message for a failed assuan-Transact and return a + gpg error code. No error is printed if RC is 0. */ +static gpg_error_t +test_transact (int rc, const char *command) +{ + if (!rc) + return 0; + log_error ("sending command `%s' to agent failed: %s\n", + command, assuan_strerror (rc)); + return gpg_error (GPG_ERR_CARD); +} + + +/* Try to open a card using an already running agent. Prepare a + proper application context and return it. */ +static app_t +open_card_via_agent (int *scd_available) +{ + assuan_context_t ctx; + app_t app; + struct agent_card_info_s info; + int rc; + + *scd_available = 0; + ctx = agent_open (1); + if (!ctx) + return NULL; + + /* Request the serialbnumber of the card. If we get + NOT_SUPPORTED or NO_SCDAEMON back, the gpg-agent either has + disabled scdaemon or it can't be used. We close the connection + in this case and use our own code. This may happen if just the + gpg-agent has been installed for the sake of passphrase + caching. */ + memset (&info, 0, sizeof info); + rc = assuan_transact (ctx, "SCD SERIALNO openpgp", + NULL, NULL, NULL, NULL, + learn_status_cb, &info); + if (rc) + { + if ((rc & 0xffff) == 60 || (rc & 0xffff) == 119) + ; /* No scdaemon available to gpg-agent. */ + else + { + write_status_text (STATUS_CARDCTRL, "4"); + log_info ("selecting openpgp failed: %s\n", assuan_strerror (rc)); + *scd_available = 1; + } + agent_release_card_info (&info); + agent_close (ctx); + return NULL; + } + + app = xcalloc (1, sizeof *app); + app->assuan_ctx = ctx; + + return app; +} + + + /* Open the current card and select the openpgp application. Return an APP context handle to be used for further procesing or NULL on error or if no OpenPGP application exists.*/ -static APP +static app_t open_card (void) { int slot = -1; int rc; - APP app; + app_t app; int did_shutdown = 0; + int scd_available; + /* First check whether we can contact a gpg-agent and divert all + operation to it. This is required because gpg as well as the + agent require exclusive access to the reader. */ + app = open_card_via_agent (&scd_available); + if (app) + goto ready; /* Yes, there is a agent with a usable card, go that way. */ + if (scd_available) + return NULL; /* agent avilabale but card problem. */ + + + /* No agent or usable agent, thus we do it on our own. */ card_close (); - retry: if (did_shutdown) @@ -309,6 +396,7 @@ open_card (void) return NULL; } + ready: app->initialized = 1; current_app = app; if (is_status_enabled () ) @@ -327,15 +415,19 @@ open_card (void) return app; } + void card_close (void) { if (current_app) { - APP app = current_app; + app_t app = current_app; current_app = NULL; - apdu_close_reader (app->slot); + if (app->assuan_ctx) + agent_close (app->assuan_ctx); + else + apdu_close_reader (app->slot); xfree (app); } } @@ -382,7 +474,7 @@ format_cacheid (const char *sn) card context will be closed in all cases except for 0 as return value and if it was possible to merely shutdown the reader. */ static int -check_card_serialno (APP app, const char *serialno) +check_card_serialno (app_t app, const char *serialno) { const char *s; int ask = 0; @@ -505,7 +597,7 @@ store_serialno (const char *line) -static int +static assuan_error_t learn_status_cb (void *opaque, const char *line) { struct agent_card_info_s *parm = opaque; @@ -644,7 +736,7 @@ learn_status_cb (void *opaque, const char *line) int agent_learn (struct agent_card_info_s *info) { - APP app; + app_t app; int rc; struct ctrl_ctx_s ctrl; time_t stamp; @@ -655,35 +747,68 @@ agent_learn (struct agent_card_info_s *info) return gpg_error (GPG_ERR_CARD); memset (info, 0, sizeof *info); - memset (&ctrl, 0, sizeof ctrl); - ctrl.status_cb = learn_status_cb; - ctrl.status_cb_arg = info; - rc = app_get_serial_and_stamp (app, &serial, &stamp); - if (!rc) + if (app->assuan_ctx) { - send_status_info (&ctrl, "SERIALNO", serial, strlen(serial), NULL, 0); - xfree (serial); - rc = app->fnc.learn_status (app, &ctrl); + rc = assuan_transact (app->assuan_ctx, "SCD LEARN --force", + NULL, NULL, NULL, NULL, + learn_status_cb, info); + rc = test_transact (rc, "SCD LEARN"); + } + else + { + memset (&ctrl, 0, sizeof ctrl); + ctrl.status_cb = learn_status_cb; + ctrl.status_cb_arg = info; + + rc = app_get_serial_and_stamp (app, &serial, &stamp); + if (!rc) + { + send_status_info (&ctrl, "SERIALNO", + serial, strlen(serial), NULL, 0); + xfree (serial); + rc = app->fnc.learn_status (app, &ctrl); + } } return rc; } -/* Get an attribite from the card. Make sure info is initialized. */ + +/* Get an attribute from the card. Make sure info is initialized. */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { - APP app; + int rc; + app_t app; struct ctrl_ctx_s ctrl; app = current_app? current_app : open_card (); if (!app) return gpg_error (GPG_ERR_CARD); - ctrl.status_cb = learn_status_cb; - ctrl.status_cb_arg = info; - return app->fnc.getattr (app, &ctrl, name); + if (app->assuan_ctx) + { + char line[ASSUAN_LINELENGTH]; + + /* We assume that NAME does not need escaping. */ + if (12 + strlen (name) > DIM(line)-1) + return gpg_error (GPG_ERR_CARD); + stpcpy (stpcpy (line, "SCD GETATTR "), name); + + rc = test_transact (assuan_transact (app->assuan_ctx, line, + NULL, NULL, NULL, NULL, + learn_status_cb, info), + "SCD GETATTR"); + } + else + { + ctrl.status_cb = learn_status_cb; + ctrl.status_cb_arg = info; + rc = app->fnc.getattr (app, &ctrl, name); + } + + return rc; } @@ -797,14 +922,47 @@ int agent_scd_setattr (const char *name, const unsigned char *value, size_t valuelen) { - APP app; + app_t app; int rc; app = current_app? current_app : open_card (); if (!app) return gpg_error (GPG_ERR_CARD); - rc = app->fnc.setattr (app, name, pin_cb, NULL, value, valuelen); + if (app->assuan_ctx) + { + char line[ASSUAN_LINELENGTH]; + char *p; + + /* We assume that NAME does not need escaping. */ + if (12 + strlen (name) > DIM(line)-1) + return gpg_error (GPG_ERR_CARD); + p = stpcpy (stpcpy (line, "SCD SETATTR "), name); + *p++ = ' '; + for (; valuelen; value++, valuelen--) + { + if (p >= line + DIM(line)-5 ) + return gpg_error (GPG_ERR_CARD); + if (*value < ' ' || *value == '+' || *value == '%') + { + sprintf (p, "%%%02X", *value); + p += 3; + } + else if (*value == ' ') + *p++ = '+'; + else + *p++ = *value; + } + *p = 0; + + rc = test_transact (assuan_transact (app->assuan_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL), + "SCD SETATTR"); + } + else + { + rc = app->fnc.setattr (app, name, pin_cb, NULL, value, valuelen); + } if (rc) write_status (STATUS_SC_OP_FAILURE); @@ -812,7 +970,7 @@ agent_scd_setattr (const char *name, } -static int +static assuan_error_t genkey_status_cb (void *opaque, const char *line) { struct agent_card_genkey_s *parm = opaque; @@ -868,8 +1026,8 @@ genkey_status_cb (void *opaque, const char *line) int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force) { - APP app; - char keynostr[20]; + app_t app; + char line[ASSUAN_LINELENGTH]; struct ctrl_ctx_s ctrl; int rc; @@ -878,18 +1036,44 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force) return gpg_error (GPG_ERR_CARD); memset (info, 0, sizeof *info); - sprintf (keynostr, "%d", keyno); - ctrl.status_cb = genkey_status_cb; - ctrl.status_cb_arg = info; - rc = app->fnc.genkey (app, &ctrl, keynostr, - force? 1:0, - pin_cb, NULL); + if (app->assuan_ctx) + { + snprintf (line, DIM(line)-1, "SCD GENKEY %s%d", + force? "--force ":"", keyno); + line[DIM(line)-1] = 0; + rc = test_transact (assuan_transact (app->assuan_ctx, line, + NULL, NULL, NULL, NULL, + genkey_status_cb, info), + "SCD GENKEY"); + } + else + { + snprintf (line, DIM(line)-1, "%d", keyno); + ctrl.status_cb = genkey_status_cb; + ctrl.status_cb_arg = info; + rc = app->fnc.genkey (app, &ctrl, line, + force? 1:0, + pin_cb, NULL); + } + if (rc) write_status (STATUS_SC_OP_FAILURE); return rc; } + +static assuan_error_t +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + membuf_t *data = opaque; + + if (buffer) + put_membuf (data, buffer, length); + return 0; +} + + /* Send a PKSIGN command to the SCdaemon. */ int agent_scd_pksign (const char *serialno, int hashalgo, @@ -897,7 +1081,7 @@ agent_scd_pksign (const char *serialno, int hashalgo, unsigned char **r_buf, size_t *r_buflen) { struct pincb_parm_s parm; - APP app; + app_t app; int rc; *r_buf = NULL; @@ -909,20 +1093,56 @@ agent_scd_pksign (const char *serialno, int hashalgo, if (!app) return gpg_error (GPG_ERR_CARD); - /* Check that the card's serialnumber is as required.*/ - rc = check_card_serialno (app, serialno); - if (rc == -1) - goto retry; + if (app->assuan_ctx) + { + char *p, line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + int i; + + if (indatalen*2 + 50 > DIM(line)) + return gpg_error (GPG_ERR_GENERAL); + + p = stpcpy (line, "SCD SETDATA "); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = test_transact (assuan_transact (app->assuan_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL), + "SCD SETDATA"); + if (!rc) + { + init_membuf (&data, 1024); + snprintf (line, DIM(line)-1, "SCD PKSIGN %s", serialno); + line[DIM(line)-1] = 0; + rc = test_transact (assuan_transact (app->assuan_ctx, line, + membuf_data_cb, &data, + NULL, NULL, NULL, NULL), + "SCD PKSIGN"); + if (rc) + xfree (get_membuf (&data, &len)); + else + *r_buf = get_membuf (&data, r_buflen); + } + } + else + { + /* Check that the card's serialnumber is as required.*/ + rc = check_card_serialno (app, serialno); + if (rc == -1) + goto retry; + + if (!rc) + rc = app->fnc.sign (app, serialno, hashalgo, + pin_cb, &parm, + indata, indatalen, + r_buf, r_buflen); + } - if (!rc) - rc = app->fnc.sign (app, serialno, hashalgo, - pin_cb, &parm, - indata, indatalen, - r_buf, r_buflen); if (rc) { write_status (STATUS_SC_OP_FAILURE); - agent_clear_pin_cache (serialno); + if (!app->assuan_ctx) + agent_clear_pin_cache (serialno); } return rc; } @@ -935,7 +1155,7 @@ agent_scd_pkdecrypt (const char *serialno, unsigned char **r_buf, size_t *r_buflen) { struct pincb_parm_s parm; - APP app; + app_t app; int rc; *r_buf = NULL; @@ -947,20 +1167,56 @@ agent_scd_pkdecrypt (const char *serialno, if (!app) return gpg_error (GPG_ERR_CARD); - /* Check that the card's serialnumber is as required.*/ - rc = check_card_serialno (app, serialno); - if (rc == -1) - goto retry; + if (app->assuan_ctx) + { + char *p, line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + int i; + + if (indatalen*2 + 50 > DIM(line)) + return gpg_error (GPG_ERR_GENERAL); + + p = stpcpy (line, "SCD SETDATA "); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = test_transact (assuan_transact (app->assuan_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL), + "SCD SETDATA"); + if (!rc) + { + init_membuf (&data, 1024); + snprintf (line, DIM(line)-1, "SCD PKDECRYPT %s", serialno); + line[DIM(line)-1] = 0; + rc = test_transact (assuan_transact (app->assuan_ctx, line, + membuf_data_cb, &data, + NULL, NULL, NULL, NULL), + "SCD PKDECRYPT"); + if (rc) + xfree (get_membuf (&data, &len)); + else + *r_buf = get_membuf (&data, r_buflen); + } + } + else + { + /* Check that the card's serialnumber is as required.*/ + rc = check_card_serialno (app, serialno); + if (rc == -1) + goto retry; + + if (!rc) + rc = app->fnc.decipher (app, serialno, + pin_cb, &parm, + indata, indatalen, + r_buf, r_buflen); + } - if (!rc) - rc = app->fnc.decipher (app, serialno, - pin_cb, &parm, - indata, indatalen, - r_buf, r_buflen); if (rc) { write_status (STATUS_SC_OP_FAILURE); - agent_clear_pin_cache (serialno); + if (!app->assuan_ctx) + agent_clear_pin_cache (serialno); } return rc; } @@ -969,7 +1225,7 @@ agent_scd_pkdecrypt (const char *serialno, int agent_scd_change_pin (int chvno) { - APP app; + app_t app; char chvnostr[20]; int reset = 0; int rc; @@ -981,9 +1237,17 @@ agent_scd_change_pin (int chvno) if (!app) return gpg_error (GPG_ERR_CARD); - sprintf (chvnostr, "%d", chvno); - rc = app->fnc.change_pin (app, NULL, chvnostr, reset, - pin_cb, NULL); + if (app->assuan_ctx) + { + rc = gpg_error (GPG_ERR_CARD); + } + else + { + sprintf (chvnostr, "%d", chvno); + rc = app->fnc.change_pin (app, NULL, chvnostr, reset, + pin_cb, NULL); + } + if (rc) write_status (STATUS_SC_OP_FAILURE); return rc; @@ -995,14 +1259,22 @@ agent_scd_change_pin (int chvno) int agent_scd_checkpin (const char *serialnobuf) { - APP app; + app_t app; int rc; app = current_app? current_app : open_card (); if (!app) return gpg_error (GPG_ERR_CARD); - rc = app->fnc.check_pin (app, serialnobuf, pin_cb, NULL); + if (app->assuan_ctx) + { + rc = gpg_error (GPG_ERR_CARD); + } + else + { + rc = app->fnc.check_pin (app, serialnobuf, pin_cb, NULL); + } + if (rc) write_status (STATUS_SC_OP_FAILURE); return rc; @@ -1017,16 +1289,24 @@ agent_openpgp_storekey (int keyno, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen) { - APP app; + app_t app; int rc; app = current_app? current_app : open_card (); if (!app) return gpg_error (GPG_ERR_CARD); - rc = app_openpgp_storekey (app, keyno, template, template_len, - created_at, m, mlen, e, elen, - pin_cb, NULL); + if (app->assuan_ctx) + { + rc = gpg_error (GPG_ERR_CARD); + } + else + { + rc = app_openpgp_storekey (app, keyno, template, template_len, + created_at, m, mlen, e, elen, + pin_cb, NULL); + } + if (rc) write_status (STATUS_SC_OP_FAILURE); return rc; diff --git a/g10/ccid-driver.c b/g10/ccid-driver.c index b817452b1..8ed9c27c5 100644 --- a/g10/ccid-driver.c +++ b/g10/ccid-driver.c @@ -1814,7 +1814,7 @@ ccid_transceive (ccid_driver_t handle, msg = send_buffer; tpdulen = last_tpdulen; } - else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns) + else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) { /* Reponse does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; diff --git a/g10/keydb.h b/g10/keydb.h index eca817ea0..81caf4993 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -26,6 +26,9 @@ #include "global.h" #include "packet.h" #include "cipher.h" +#ifdef ENABLE_AGENT_SUPPORT +#include "assuan.h" +#endif /* What qualifies as a certification (rather than a signature?) */ #define IS_CERT(s) (IS_KEY_SIG(s) || IS_UID_SIG(s) || IS_SUBKEY_SIG(s) \ @@ -184,6 +187,10 @@ int build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, unsigned use ); /*-- passphrase.h --*/ +#ifdef ENABLE_AGENT_SUPPORT +assuan_context_t agent_open (int try); +void agent_close (assuan_context_t ctx); +#endif int have_static_passphrase(void); void read_passphrase_from_fd( int fd ); void passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ); diff --git a/g10/main.h b/g10/main.h index 562e22a2f..38c999bae 100644 --- a/g10/main.h +++ b/g10/main.h @@ -275,6 +275,7 @@ void pause_on_sigusr( int which ); void block_all_signals(void); void unblock_all_signals(void); + #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); diff --git a/g10/passphrase.c b/g10/passphrase.c index a68d564fe..030d801b2 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -278,12 +278,13 @@ agent_send_all_options (assuan_context_t ctx) /* * Open a connection to the agent and initializes the connection. - * Returns: -1 on error; on success a file descriptor for that - * connection is returned. + * Returns: -1 on error; on success an Assuan context for that + * connection is returned. With TRY set to true, no error messages + * are printed and the use of the agent won't get disabled on failure. */ #ifdef ENABLE_AGENT_SUPPORT -static assuan_context_t -agent_open (void) +assuan_context_t +agent_open (int try) { int rc; assuan_context_t ctx; @@ -298,8 +299,11 @@ agent_open (void) infostr = getenv ( "GPG_AGENT_INFO" ); if (!infostr || !*infostr) { - log_error (_("gpg-agent is not available in this session\n")); - opt.use_agent = 0; + if (!try) + { + log_error (_("gpg-agent is not available in this session\n")); + opt.use_agent = 0; + } return NULL; } infostr = xstrdup ( infostr ); @@ -307,9 +311,12 @@ agent_open (void) if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) { - log_error ( _("malformed GPG_AGENT_INFO environment variable\n")); + if (!try) + { + log_error ( _("malformed GPG_AGENT_INFO environment variable\n")); + opt.use_agent = 0; + } xfree (infostr); - opt.use_agent = 0; return NULL; } *p++ = 0; @@ -319,28 +326,38 @@ agent_open (void) prot = *p? atoi (p+1) : 0; if (prot != 1) { - log_error (_("gpg-agent protocol version %d is not supported\n"), prot); + if (!try) + { + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + opt.use_agent = 0; + } xfree (infostr); - opt.use_agent = 0; return NULL; } rc = assuan_socket_connect (&ctx, infostr, pid); if (rc) { - log_error ( _("can't connect to `%s': %s\n"), - infostr, assuan_strerror (rc)); + if (!try) + { + log_error ( _("can't connect to `%s': %s\n"), + infostr, assuan_strerror (rc)); + opt.use_agent = 0; + } xfree (infostr ); - opt.use_agent = 0; return NULL; } xfree (infostr); if (agent_send_all_options (ctx)) { - log_error (_("problem with the agent - disabling agent use\n")); + if (!try) + { + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; + } assuan_disconnect (ctx); - opt.use_agent = 0; return NULL; } @@ -350,7 +367,7 @@ agent_open (void) #ifdef ENABLE_AGENT_SUPPORT -static void +void agent_close (assuan_context_t ctx) { assuan_disconnect (ctx); @@ -474,7 +491,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *cacheid, } #endif - if ( !(ctx = agent_open ()) ) + if ( !(ctx = agent_open (0)) ) goto failure; if (custom_description) @@ -672,7 +689,7 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) else pk = NULL; - if ( !(ctx = agent_open ()) ) + if ( !(ctx = agent_open (0)) ) goto failure; { diff --git a/g10/tlv.c b/g10/tlv.c index b7c8819e6..3a81ea6d9 100644 --- a/g10/tlv.c +++ b/g10/tlv.c @@ -1,5 +1,5 @@ /* tlv.c - Tag-Length-Value Utilities - * Copyright (C) 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,8 @@ #include #include -#include "errors.h" -#include "util.h" -#include "packet.h" +#include + #include "tlv.h" static const unsigned char * @@ -114,17 +113,32 @@ do_find_tlv (const unsigned char *buffer, size_t length, /* Locate a TLV encoded data object in BUFFER of LENGTH and return a pointer to value as well as its length in NBYTES. Return - NULL if it was not found. Note, that the function does not check - whether the value fits into the provided buffer. */ + NULL if it was not found or if the object does not fit into the buffer. */ const unsigned char * find_tlv (const unsigned char *buffer, size_t length, int tag, size_t *nbytes) { - return do_find_tlv (buffer, length, tag, nbytes, 0); + const unsigned char *p; + + p = do_find_tlv (buffer, length, tag, nbytes, 0); + if (p && *nbytes > (length - (p-buffer))) + p = NULL; /* Object longer than buffer. */ + return p; } +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +const unsigned char * +find_tlv_unchecked (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + return do_find_tlv (buffer, length, tag, nbytes, 0); +} + /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE diff --git a/g10/tlv.h b/g10/tlv.h index f1ef13415..628580431 100644 --- a/g10/tlv.h +++ b/g10/tlv.h @@ -21,8 +21,6 @@ #ifndef SCD_TLV_H #define SCD_TLV_H 1 -#include "types.h" -#include "cardglue.h" enum tlv_tag_class { CLASS_UNIVERSAL = 0, @@ -64,13 +62,20 @@ enum tlv_tag_type { }; +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found or if the object does not fit into the buffer. */ +const unsigned char *find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes); + /* Locate a TLV encoded data object in BUFFER of LENGTH and return a pointer to value as well as its length in NBYTES. Return NULL if it was not found. Note, that the function does not check whether the value fits into the provided buffer.*/ -const unsigned char *find_tlv (const unsigned char *buffer, size_t length, - int tag, size_t *nbytes); +const unsigned char *find_tlv_unchecked (const unsigned char *buffer, + size_t length, + int tag, size_t *nbytes); /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag