(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.
This commit is contained in:
Werner Koch 2005-05-20 20:37:08 +00:00
parent 780331cfcd
commit bd644c8d45
10 changed files with 1087 additions and 226 deletions

View File

@ -1,3 +1,21 @@
2005-05-20 Werner Koch <wk@g10code.com>
* 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 <wk@g10code.com>
* app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer
version in gnupg 1.9 CVS.
2005-05-18 Werner Koch <wk@g10code.com>
* 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 <dshaw@jabberwocky.com>
* build-packet.c (do_comment): Removed.

View File

@ -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 <ksba.h>
#if GNUPG_MAJOR_VERSION == 1
# ifdef ENABLE_AGENT_SUPPORT
# include "assuan.h"
# endif
#else
# include <ksba.h>
#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

View File

@ -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 "<serialno>[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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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 );

View File

@ -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);

View File

@ -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;
{

View File

@ -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 <string.h>
#include <assert.h>
#include "errors.h"
#include "util.h"
#include "packet.h"
#include <gpg-error.h>
#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

View File

@ -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