1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-08 12:44:23 +01:00

A bunch of changes for the openpgp card.

This commit is contained in:
Werner Koch 2004-04-27 08:23:45 +00:00
parent 0c67c75cbe
commit 577d9c2342
19 changed files with 1958 additions and 263 deletions

View File

@ -1,3 +1,63 @@
2004-04-27 Werner Koch <wk@gnupg.org>
* g10.c: New commands --allow-admin and --deny-admin.
* options.h (opt): Add member ALLOW_ADMIN.
* tlv.h, tlv.c: New. Copied from gnupg-1.9.
* cardglue.c (open_card): The serialno is now set internally by
app_select_openpgp; changed invocation.
* cardglue.h (app_t, ctrl_t): New.
(GPG_ERR_EBUSY, GPG_ERR_ENOENT, GPG_ERR_NOT_FOUND, GPG_ERR_BUG)
(GPG_ERR_NOT_IMPLEMENTED, GPG_ERR_EACCESS): New.
(gpg_err_code_from_errno): New.
* app-common.h, app-openpgp.c, iso7816.c, iso7816.h
* apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
* card-util.c: Updated from current gnupg-1.9.
Changes are:
* app-common.h: New members FNC.DEINIT and APP_LOCAL.
* app-openpgp.c (do_deinit): New.
(get_cached_data, flush_cache_item, flush_cache_after_error)
(flush_cache): New.
(get_one_do): Replaced arg SLOT by APP. Make used of cached data.
(verify_chv2, verify_chv3): Flush some cache item after error.
(do_change_pin): Ditto.
(do_sign): Ditto.
(do_setattr): Flush cache item.
(do_genkey): Flush the entire cache.
(compare_fingerprint): Use cached data.
* apdu.c (apdu_send_le): Reinitialize RESULTLEN. Handle
SW_EOF_REACHED like SW_SUCCESS.
* ccid-driver.c (parse_ccid_descriptor): Store some of the reader
features away. New arg HANDLE
(read_device_info): New arg HANDLE. Changed caller.
(bulk_in): Handle time extension requests.
(ccid_get_atr): Setup parameters and the IFSD.
(compute_edc): New. Factored out code.
(ccid_transceive): Use default NADs when required.
* apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
* iso7816.c (map_sw): Map it.
* ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
* apdu.c (apdu_get_status): New.
(ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
(get_status_ccid): New.
(apdu_reset): New.
(reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
(reset_ccid_reader): New.
(apdu_enum_reader): New.
* apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
(new_reader_slot) [USE_GNU_PTH]: Init mutex.
(apdu_reset, apdu_get_status, apdu_send_le): Run functions
in locked mode.
2004-04-25 David Shaw <dshaw@jabberwocky.com> 2004-04-25 David Shaw <dshaw@jabberwocky.com>
* getkey.c (get_seckey_byname2): Significantly simplify this * getkey.c (get_seckey_byname2): Significantly simplify this
@ -131,6 +191,11 @@
* options.h: Encapsulate keyserver details. Change all callers. * options.h: Encapsulate keyserver details. Change all callers.
2004-04-05 Werner Koch <wk@gnupg.org>
* status.h (STATUS_NEWSIG): New.
* status.c (get_status_string): Add it.
2004-03-27 David Shaw <dshaw@jabberwocky.com> 2004-03-27 David Shaw <dshaw@jabberwocky.com>
* keyedit.c (keyedit_menu): Request a trustdb update when adding a * keyedit.c (keyedit_menu): Request a trustdb update when adding a

View File

@ -78,7 +78,7 @@ card_support_source_scd = \
iso7816.c iso7816.h \ iso7816.c iso7816.h \
apdu.c apdu.h \ apdu.c apdu.h \
ccid-driver.c ccid-driver.h ccid-driver.c ccid-driver.h
card_support_source_local = cardglue.c cardglue.h card_support_source_local = cardglue.c cardglue.h tlv.c tlv.h
else else
card_support_source_g10 = card_support_source_g10 =
card_support_source_scd = card_support_source_scd =

View File

@ -1,5 +1,5 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O /* apdu.c - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -24,11 +24,18 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#ifdef USE_GNU_PTH
# include <pth.h>
# include <unistd.h>
# include <fcntl.h>
#endif
#ifdef HAVE_OPENSC #ifdef HAVE_OPENSC
# include <opensc/opensc.h> # include <opensc/opensc.h>
#endif #endif
#if GNUPG_MAJOR_VERSION == 1 #if defined(GNUPG_SCD_MAIN_HEADER)
#include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source /* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over copied from the current GnuPG >= 1.9 and is maintained over
there. */ there. */
@ -46,6 +53,11 @@
#include "dynload.h" #include "dynload.h"
#include "ccid-driver.h" #include "ccid-driver.h"
#ifdef USE_GNU_PTH
#define NEED_PCSC_WRAPPER 1
#endif
#define MAX_READER 4 /* Number of readers we support concurrently. */ #define MAX_READER 4 /* Number of readers we support concurrently. */
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for #define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
insertion of the card (1 = don't wait). */ insertion of the card (1 = don't wait). */
@ -57,6 +69,12 @@
#define DLSTDCALL #define DLSTDCALL
#endif #endif
#ifdef _POSIX_OPEN_MAX
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
#else
#define MAX_OPEN_FDS 20
#endif
/* A structure to collect information pertaining to one reader /* A structure to collect information pertaining to one reader
slot. */ slot. */
@ -72,6 +90,11 @@ struct reader_table_s {
unsigned long context; unsigned long context;
unsigned long card; unsigned long card;
unsigned long protocol; unsigned long protocol;
#ifdef NEED_PCSC_WRAPPER
int req_fd;
int rsp_fd;
pid_t pid;
#endif /*NEED_PCSC_WRAPPER*/
} pcsc; } pcsc;
#ifdef HAVE_OPENSC #ifdef HAVE_OPENSC
int is_osc; /* We are using the OpenSC driver layer. */ int is_osc; /* We are using the OpenSC driver layer. */
@ -83,6 +106,11 @@ struct reader_table_s {
int status; int status;
unsigned char atr[33]; unsigned char atr[33];
size_t atrlen; size_t atrlen;
unsigned int change_counter;
#ifdef USE_GNU_PTH
int lock_initialized;
pth_mutex_t lock;
#endif
}; };
typedef struct reader_table_s *reader_table_t; typedef struct reader_table_s *reader_table_t;
@ -183,11 +211,27 @@ new_reader_slot (void)
log_error ("new_reader_slot: out of slots\n"); log_error ("new_reader_slot: out of slots\n");
return -1; return -1;
} }
#ifdef USE_GNU_PTH
if (!reader_table[reader].lock_initialized)
{
if (!pth_mutex_init (&reader_table[reader].lock))
{
log_error ("error initializing mutex: %s\n", strerror (errno));
return -1;
}
reader_table[reader].lock_initialized = 1;
}
#endif /*USE_GNU_PTH*/
reader_table[reader].used = 1; reader_table[reader].used = 1;
reader_table[reader].is_ccid = 0; reader_table[reader].is_ccid = 0;
reader_table[reader].is_ctapi = 0; reader_table[reader].is_ctapi = 0;
#ifdef HAVE_OPENSC #ifdef HAVE_OPENSC
reader_table[reader].is_osc = 0; reader_table[reader].is_osc = 0;
#endif
#ifdef NEED_PCSC_WRAPPER
reader_table[reader].pcsc.req_fd = -1;
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif #endif
return reader; return reader;
} }
@ -368,6 +412,18 @@ close_ct_reader (int slot)
return 0; return 0;
} }
static int
reset_ct_reader (int slot)
{
return SW_HOST_NOT_SUPPORTED;
}
static int
ct_get_status (int slot, unsigned int *status)
{
return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a /* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual retruned size will be maximum of *BUFLEN data in BUFFER, the actual retruned size will be
@ -395,6 +451,66 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifdef NEED_PCSC_WRAPPER
static int
writen (int fd, const void *buf, size_t nbytes)
{
size_t nleft = nbytes;
int nwritten;
/* log_printhex (" writen:", buf, nbytes); */
while (nleft > 0)
{
#ifdef USE_GNU_PTH
nwritten = pth_write (fd, buf, nleft);
#else
nwritten = write (fd, buf, nleft);
#endif
if (nwritten < 0 && errno == EINTR)
continue;
if (nwritten < 0)
return -1;
nleft -= nwritten;
buf = (const char*)buf + nwritten;
}
return 0;
}
/* Read up to BUFLEN bytes from FD and return the number of bytes
actually read in NREAD. Returns -1 on error or 0 on success. */
static int
readn (int fd, void *buf, size_t buflen, size_t *nread)
{
size_t nleft = buflen;
int n;
/* void *orig_buf = buf; */
while (nleft > 0)
{
#ifdef USE_GNU_PTH
n = pth_read (fd, buf, nleft);
#else
n = read (fd, buf, nleft);
#endif
if (n < 0 && errno == EINTR)
continue;
if (n < 0)
return -1; /* read error. */
if (!n)
break; /* EOF */
nleft -= n;
buf = (char*)buf + n;
}
if (nread)
*nread = buflen - nleft;
/* log_printhex (" readn:", orig_buf, *nread); */
return 0;
}
#endif /*NEED_PCSC_WRAPPER*/
static const char * static const char *
pcsc_error_string (long err) pcsc_error_string (long err)
{ {
@ -455,6 +571,172 @@ pcsc_error_string (long err)
static int static int
open_pcsc_reader (const char *portstr) open_pcsc_reader (const char *portstr)
{ {
#ifdef NEED_PCSC_WRAPPER
/* Open the PC/SC reader using the pcsc_wrapper program. This is
needed to cope with different thread models and other peculiarities
of libpcsclite. */
int slot;
reader_table_t slotp;
int fd, rp[2], wp[2];
int n, i;
pid_t pid;
size_t len;
unsigned char msgbuf[9];
int err;
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
/* Fire up the pcsc wrapper. We don't use any fork/exec code from
the common directy but implement it direclty so that this file
may still be source copied. */
if (pipe (rp) == -1)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
slotp->used = 0;
return -1;
}
if (pipe (wp) == -1)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
close (rp[0]);
close (rp[1]);
slotp->used = 0;
return -1;
}
pid = fork ();
if (pid == -1)
{
log_error ("error forking process: %s\n", strerror (errno));
close (rp[0]);
close (rp[1]);
close (wp[0]);
close (wp[1]);
slotp->used = 0;
return -1;
}
slotp->pcsc.pid = pid;
if (!pid)
{ /*
=== Child ===
*/
/* Double fork. */
pid = fork ();
if (pid == -1)
_exit (31);
if (pid)
_exit (0); /* Immediate exit this parent, so that the child
gets cleaned up by the init process. */
/* Connect our pipes. */
if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
/* Send stderr to the bit bucket. */
fd = open ("/dev/null", O_WRONLY);
if (fd == -1)
log_fatal ("can't open `/dev/null': %s", strerror (errno));
if (fd != 2 && dup2 (fd, 2) == -1)
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
/* Close all other files. */
n = sysconf (_SC_OPEN_MAX);
if (n < 0)
n = MAX_OPEN_FDS;
for (i=3; i < n; i++)
close(i);
errno = 0;
execl (GNUPG_LIBDIR "/pcsc-wrapper",
"pcsc-wrapper",
"--",
"1", /* API version */
opt.pcsc_driver, /* Name of the PC/SC library. */
NULL);
_exit (31);
}
/*
=== Parent ===
*/
close (wp[0]);
close (rp[1]);
slotp->pcsc.req_fd = wp[1];
slotp->pcsc.rsp_fd = rp[0];
/* Wait for the intermediate child to terminate. */
while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
;
/* Now send the open request. */
msgbuf[0] = 0x01; /* OPEN command. */
len = portstr? strlen (portstr):0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|| (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
{
log_error ("error sending PC/SC OPEN request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC OPEN response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
if (len > DIM (slotp->atr))
{
log_error ("PC/SC returned a too large ATR (len=%x)\n", len);
goto command_failed;
}
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
if (err)
{
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
goto command_failed;
}
n = len;
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
{
log_error ("error receiving PC/SC OPEN response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
slotp->atrlen = len;
dump_reader_status (slot);
return slot;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return -1;
#else /*!NEED_PCSC_WRAPPER */
long err; long err;
int slot; int slot;
char *list = NULL; char *list = NULL;
@ -557,9 +839,16 @@ open_pcsc_reader (const char *portstr)
dump_reader_status (slot); dump_reader_status (slot);
return slot; return slot;
#endif /*!NEED_PCSC_WRAPPER */
} }
static int
pcsc_get_status (int slot, unsigned int *status)
{
return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a /* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: CT API error code. */ set to BUFLEN. Returns: CT API error code. */
@ -567,6 +856,109 @@ static int
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen) unsigned char *buffer, size_t *buflen)
{ {
#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len, full_len;
int i, n;
unsigned char msgbuf[9];
if (DBG_CARD_IO)
log_printhex (" PCSC_data:", apdu, apdulen);
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
return -1;
}
msgbuf[0] = 0x03; /* TRANSMIT command. */
len = apdulen;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|| writen (slotp->pcsc.req_fd, apdu, len))
{
log_error ("error sending PC/SC TRANSMIT request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
if (err)
{
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return -1;
}
full_len = len;
n = *buflen < len ? *buflen : len;
if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
*buflen = n;
full_len -= len;
if (full_len)
{
log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
err = -1;
}
/* We need to read any rest of the response, to keep the
protocol runnng. */
while (full_len)
{
unsigned char dummybuf[128];
n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
full_len -= n;
}
return err;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return -1;
#else /*!NEED_PCSC_WRAPPER*/
long err; long err;
struct pcsc_io_request_s send_pci; struct pcsc_io_request_s send_pci;
unsigned long recv_len; unsigned long recv_len;
@ -589,14 +981,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
pcsc_error_string (err), err); pcsc_error_string (err), err);
return err? -1:0; /* FIXME: Return appropriate error code. */ return err? -1:0; /* FIXME: Return appropriate error code. */
#endif /*!NEED_PCSC_WRAPPER*/
} }
static int static int
close_pcsc_reader (int slot) close_pcsc_reader (int slot)
{ {
#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len;
int i;
unsigned char msgbuf[9];
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
return 0;
}
msgbuf[0] = 0x02; /* CLOSE command. */
len = 0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
{
log_error ("error sending PC/SC CLOSE request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC CLOSE response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
if (err)
log_error ("pcsc_close failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
/* We will the wrapper in any case - errors are merely
informational. */
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return 0;
#else /*!NEED_PCSC_WRAPPER*/
pcsc_release_context (reader_table[slot].pcsc.context); pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0; reader_table[slot].used = 0;
return 0; return 0;
#endif /*!NEED_PCSC_WRAPPER*/
}
static int
reset_pcsc_reader (int slot)
{
return SW_HOST_NOT_SUPPORTED;
} }
@ -659,6 +1124,46 @@ close_ccid_reader (int slot)
} }
static int
reset_ccid_reader (int slot)
{
int err;
reader_table_t slotp = reader_table + slot;
unsigned char atr[33];
size_t atrlen;
err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
if (err)
return -1;
/* If the reset was successful, update the ATR. */
assert (sizeof slotp->atr >= sizeof atr);
slotp->atrlen = atrlen;
memcpy (slotp->atr, atr, atrlen);
dump_reader_status (slot);
return 0;
}
static int
get_status_ccid (int slot, unsigned int *status)
{
int rc;
int bits;
rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
if (rc)
return -1;
if (bits == 0)
*status = 1|2|4;
else if (bits == 1)
*status = 2;
else
*status = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a /* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be maximum of *BUFLEN data in BUFFER, the actual returned size will be
@ -796,6 +1301,18 @@ close_osc_reader (int slot)
return 0; return 0;
} }
static int
reset_osc_reader (int slot)
{
return SW_HOST_NOT_SUPPORTED;
}
static int
ocsc_get_status (int slot, unsigned int *status)
{
return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a /* Actually send the APDU of length APDULEN to SLOT and return a
@ -894,6 +1411,45 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
Driver Access Driver Access
*/ */
static int
lock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
{
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_GNU_PTH*/
return 0;
}
static int
trylock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
{
if (errno == EBUSY)
return SW_HOST_BUSY;
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_GNU_PTH*/
return 0;
}
static void
unlock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_release (&reader_table[slot].lock))
log_error ("failed to release apdu lock: %s\n", strerror (errno));
#endif /*USE_GNU_PTH*/
}
/* Open the reader and return an internal slot number or -1 on /* Open the reader and return an internal slot number or -1 on
error. If PORTSTR is NULL we default to a suitable port (for ctAPI: error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). If the first USB reader. For PC/SC the first listed reader). If
@ -935,7 +1491,7 @@ apdu_open_reader (const char *portstr)
handle = dlopen (opt.ctapi_driver, RTLD_LAZY); handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
if (!handle) if (!handle)
{ {
log_error ("apdu_open_reader: failed to open driver: %s", log_error ("apdu_open_reader: failed to open driver: %s\n",
dlerror ()); dlerror ());
return -1; return -1;
} }
@ -957,12 +1513,13 @@ apdu_open_reader (const char *portstr)
/* No ctAPI configured, so lets try the PC/SC API */ /* No ctAPI configured, so lets try the PC/SC API */
if (!pcsc_api_loaded) if (!pcsc_api_loaded)
{ {
#ifndef NEED_PCSC_WRAPPER
void *handle; void *handle;
handle = dlopen (opt.pcsc_driver, RTLD_LAZY); handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
if (!handle) if (!handle)
{ {
log_error ("apdu_open_reader: failed to open driver `%s': %s", log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
opt.pcsc_driver, dlerror ()); opt.pcsc_driver, dlerror ());
return -1; return -1;
} }
@ -1018,9 +1575,10 @@ apdu_open_reader (const char *portstr)
dlclose (handle); dlclose (handle);
return -1; return -1;
} }
#endif /*!NEED_PCSC_WRAPPER*/
pcsc_api_loaded = 1; pcsc_api_loaded = 1;
} }
return open_pcsc_reader (portstr); return open_pcsc_reader (portstr);
} }
@ -1044,6 +1602,47 @@ apdu_close_reader (int slot)
return close_pcsc_reader (slot); return close_pcsc_reader (slot);
} }
/* Enumerate all readers and return information on whether this reader
is in use. The caller should start with SLOT set to 0 and
increment it with each call until an error is returned. */
int
apdu_enum_reader (int slot, int *used)
{
if (slot < 0 || slot >= MAX_READER)
return SW_HOST_NO_DRIVER;
*used = reader_table[slot].used;
return 0;
}
/* Do a reset for the card in reader at SLOT. */
int
apdu_reset (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = lock_slot (slot)))
return sw;
if (reader_table[slot].is_ctapi)
sw = reset_ct_reader (slot);
#ifdef HAVE_LIBUSB
else if (reader_table[slot].is_ccid)
sw = reset_ccid_reader (slot);
#endif
#ifdef HAVE_OPENSC
else if (reader_table[slot].is_osc)
sw = reset_osc_reader (slot);
#endif
else
sw = reset_pcsc_reader (slot);
unlock_slot (slot);
return sw;
}
unsigned char * unsigned char *
apdu_get_atr (int slot, size_t *atrlen) apdu_get_atr (int slot, size_t *atrlen)
@ -1060,6 +1659,7 @@ apdu_get_atr (int slot, size_t *atrlen)
*atrlen = reader_table[slot].atrlen; *atrlen = reader_table[slot].atrlen;
return buf; return buf;
} }
static const char * static const char *
@ -1082,7 +1682,62 @@ error_string (int slot, long rc)
} }
/* Dispatcher for the actual send_apdu fucntion. */ /* Retrieve the status for SLOT. The function does obnly wait fot the
card to become available if HANG is set to true. On success the
bits in STATUS will be set to
bit 0 = card present and usable
bit 1 = card present
bit 2 = card active
bit 3 = card access locked [not yet implemented]
For must application, tetsing bit 0 is sufficient.
CHANGED will receive the value of the counter tracking the number
of card insertions. This value may be used to detect a card
change.
*/
int
apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed)
{
int sw;
unsigned int s;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
return sw;
if (reader_table[slot].is_ctapi)
sw = ct_get_status (slot, &s);
#ifdef HAVE_LIBUSB
else if (reader_table[slot].is_ccid)
sw = get_status_ccid (slot, &s);
#endif
#ifdef HAVE_OPENSC
else if (reader_table[slot].is_osc)
sw = osc_get_status (slot, &s);
#endif
else
sw = pcsc_get_status (slot, &s);
unlock_slot (slot);
if (sw)
return sw;
if (status)
*status = s;
if (changed)
*changed = reader_table[slot].change_counter;
return 0;
}
/* Dispatcher for the actual send_apdu function. Note, that this
function should be called in locked state. */
static int static int
send_apdu (int slot, unsigned char *apdu, size_t apdulen, send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen) unsigned char *buffer, size_t *buflen)
@ -1117,13 +1772,18 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le, int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen) unsigned char **retbuf, size_t *retbuflen)
{ {
unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */ #define RESULTLEN 256
size_t resultlen = 256; unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
the driver. */
size_t resultlen;
unsigned char apdu[5+256+1]; unsigned char apdu[5+256+1];
size_t apdulen; size_t apdulen;
int sw; int sw;
long rc; /* we need a long here due to PC/SC. */ long rc; /* we need a long here due to PC/SC. */
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (DBG_CARD_IO) if (DBG_CARD_IO)
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n", log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
class, ins, p0, p1, lc, le); class, ins, p0, p1, lc, le);
@ -1135,6 +1795,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
if ((!data && lc != -1) || (data && lc == -1)) if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE; return SW_HOST_INV_VALUE;
if ((sw = lock_slot (slot)))
return sw;
apdulen = 0; apdulen = 0;
apdu[apdulen++] = class; apdu[apdulen++] = class;
apdu[apdulen++] = ins; apdu[apdulen++] = ins;
@ -1151,11 +1814,13 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
assert (sizeof (apdu) >= apdulen); assert (sizeof (apdu) >= apdulen);
/* As safeguard don't pass any garbage from the stack to the driver. */ /* As safeguard don't pass any garbage from the stack to the driver. */
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen); rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
if (rc || resultlen < 2) if (rc || resultlen < 2)
{ {
log_error ("apdu_send_simple(%d) failed: %s\n", log_error ("apdu_send_simple(%d) failed: %s\n",
slot, error_string (slot, rc)); slot, error_string (slot, rc));
unlock_slot (slot);
return SW_HOST_INCOMPLETE_CARD_RESPONSE; return SW_HOST_INCOMPLETE_CARD_RESPONSE;
} }
sw = (result[resultlen-2] << 8) | result[resultlen-1]; sw = (result[resultlen-2] << 8) | result[resultlen-1];
@ -1168,13 +1833,16 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
log_printhex (" dump: ", result, resultlen); log_printhex (" dump: ", result, resultlen);
} }
if (sw == SW_SUCCESS) if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
{ {
if (retbuf) if (retbuf)
{ {
*retbuf = xtrymalloc (resultlen? resultlen : 1); *retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf) if (!*retbuf)
return SW_HOST_OUT_OF_CORE; {
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen; *retbuflen = resultlen;
memcpy (*retbuf, result, resultlen); memcpy (*retbuf, result, resultlen);
} }
@ -1190,7 +1858,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{ {
*retbuf = p = xtrymalloc (bufsize); *retbuf = p = xtrymalloc (bufsize);
if (!*retbuf) if (!*retbuf)
return SW_HOST_OUT_OF_CORE; {
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize); assert (resultlen < bufsize);
memcpy (p, result, resultlen); memcpy (p, result, resultlen);
p += resultlen; p += resultlen;
@ -1200,20 +1871,23 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{ {
int len = (sw & 0x00ff); int len = (sw & 0x00ff);
log_debug ("apdu_send_simple(%d): %d more bytes available\n", if (DBG_CARD_IO)
slot, len); log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
apdulen = 0; apdulen = 0;
apdu[apdulen++] = class; apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0; apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0; apdu[apdulen++] = 0;
apdu[apdulen++] = 0; apdu[apdulen++] = 0;
apdu[apdulen++] = 64; /* that is 256 bytes for Le */ apdu[apdulen++] = len;
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen); rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
if (rc || resultlen < 2) if (rc || resultlen < 2)
{ {
log_error ("apdu_send_simple(%d) for get response failed: %s\n", log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, error_string (slot, rc)); slot, error_string (slot, rc));
unlock_slot (slot);
return SW_HOST_INCOMPLETE_CARD_RESPONSE; return SW_HOST_INCOMPLETE_CARD_RESPONSE;
} }
sw = (result[resultlen-2] << 8) | result[resultlen-1]; sw = (result[resultlen-2] << 8) | result[resultlen-1];
@ -1225,16 +1899,21 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
log_printhex (" dump: ", result, resultlen); log_printhex (" dump: ", result, resultlen);
} }
if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS) if ((sw & 0xff00) == SW_MORE_DATA
|| sw == SW_SUCCESS
|| sw == SW_EOF_REACHED )
{ {
if (retbuf) if (retbuf && resultlen)
{ {
if (p - *retbuf + resultlen > bufsize) if (p - *retbuf + resultlen > bufsize)
{ {
bufsize += resultlen > 4096? resultlen: 4096; bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize); tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp) if (!tmp)
return SW_HOST_OUT_OF_CORE; {
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf); p = tmp + (p - *retbuf);
*retbuf = tmp; *retbuf = tmp;
} }
@ -1257,10 +1936,14 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
*retbuf = tmp; *retbuf = tmp;
} }
} }
unlock_slot (slot);
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS) if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
log_printhex (" dump: ", *retbuf, *retbuflen); log_printhex (" dump: ", *retbuf, *retbuflen);
return sw; return sw;
#undef RESULTLEN
} }
/* Send an APDU to the card in SLOT. The APDU is created from all /* Send an APDU to the card in SLOT. The APDU is created from all

View File

@ -27,13 +27,16 @@
enum { enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/ masked of.*/
SW_EOF_REACHED = 0x6282,
SW_EEPROM_FAILURE = 0x6581, SW_EEPROM_FAILURE = 0x6581,
SW_WRONG_LENGTH = 0x6700, SW_WRONG_LENGTH = 0x6700,
SW_CHV_WRONG = 0x6982, SW_CHV_WRONG = 0x6982,
SW_CHV_BLOCKED = 0x6983, SW_CHV_BLOCKED = 0x6983,
SW_USE_CONDITIONS = 0x6985, SW_USE_CONDITIONS = 0x6985,
SW_NOT_SUPPORTED = 0x6a81,
SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */ SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
SW_NOT_SUPPORTED = 0x6a81,
SW_FILE_NOT_FOUND = 0x6a82,
SW_RECORD_NOT_FOUND = 0x6a83,
SW_REF_NOT_FOUND = 0x6a88, SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00, SW_BAD_P0_P1 = 0x6b00,
SW_INS_NOT_SUP = 0x6d00, SW_INS_NOT_SUP = 0x6d00,
@ -45,9 +48,12 @@ enum {
those values can't be issued by a card. */ those values can't be issued by a card. */
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
between errnos on a failed malloc. */ between errnos on a failed malloc. */
SW_HOST_INV_VALUE = 0x10002, SW_HOST_INV_VALUE = 0x10002,
SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003, SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
SW_HOST_NO_DRIVER = 0x10004 SW_HOST_NO_DRIVER = 0x10004,
SW_HOST_NOT_SUPPORTED = 0x10005,
SW_HOST_LOCKING_FAILED= 0x10006,
SW_HOST_BUSY = 0x10007
}; };
@ -55,10 +61,14 @@ enum {
/* Note , that apdu_open_reader returns no status word but -1 on error. */ /* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr); int apdu_open_reader (const char *portstr);
int apdu_close_reader (int slot); int apdu_close_reader (int slot);
int apdu_enum_reader (int slot, int *used);
unsigned char *apdu_get_atr (int slot, size_t *atrlen); unsigned char *apdu_get_atr (int slot, size_t *atrlen);
/* The apdu send functions do return status words. */ /* The apdu send functions do return status words. */
int apdu_reset (int slot);
int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed);
int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data); int lc, const char *data);
int apdu_send (int slot, int class, int ins, int p0, int p1, int apdu_send (int slot, int class, int ins, int p0, int p1,

View File

@ -21,6 +21,12 @@
#ifndef GNUPG_SCD_APP_COMMON_H #ifndef GNUPG_SCD_APP_COMMON_H
#define GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H
#if GNUPG_MAJOR_VERSION != 1
#include <ksba.h>
#endif
struct app_local_s; /* Defined by all app-*.c. */
struct app_ctx_s { struct app_ctx_s {
int initialized; /* The application has been initialied and the int initialized; /* The application has been initialied and the
function pointers may be used. Note that for function pointers may be used. Note that for
@ -29,111 +35,131 @@ struct app_ctx_s {
int slot; /* Used reader. */ int slot; /* Used reader. */
unsigned char *serialno; /* Serialnumber in raw form, allocated. */ unsigned char *serialno; /* Serialnumber in raw form, allocated. */
size_t serialnolen; /* Length in octets of serialnumber. */ size_t serialnolen; /* Length in octets of serialnumber. */
const char *apptype;
unsigned int card_version; unsigned int card_version;
int did_chv1; int did_chv1;
int force_chv1; /* True if the card does not cache CHV1. */ int force_chv1; /* True if the card does not cache CHV1. */
int did_chv2; int did_chv2;
int did_chv3; int did_chv3;
struct app_local_s *app_local; /* Local to the application. */
struct { struct {
int (*learn_status) (APP app, CTRL ctrl); void (*deinit) (app_t app);
int (*getattr) (APP app, CTRL ctrl, const char *name); int (*learn_status) (app_t app, ctrl_t ctrl);
int (*setattr) (APP app, const char *name, int (*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 **), int (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *value, size_t valuelen); const unsigned char *value, size_t valuelen);
int (*sign) (APP app, int (*sign) (app_t app,
const char *keyidstr, int hashalgo, const char *keyidstr, int hashalgo,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); unsigned char **outdata, size_t *outdatalen );
int (*auth) (APP app, const char *keyidstr, int (*auth) (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
int (*decipher) (APP app, const char *keyidstr, int (*decipher) (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
int (*genkey) (APP app, CTRL ctrl, int (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags, const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
int (*change_pin) (APP app, CTRL ctrl, int (*change_pin) (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode, const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
int (*check_pin) (APP app, const char *keyidstr, int (*check_pin) (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
} fnc; } fnc;
}; };
#if GNUPG_MAJOR_VERSION == 1 #if GNUPG_MAJOR_VERSION == 1
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); int app_select_openpgp (app_t app);
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
#else #else
/*-- app-help.c --*/
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/ /*-- app.c --*/
void app_set_default_reader_port (const char *portstr); app_t select_application (ctrl_t ctrl, int slot, const char *name);
APP select_application (void); void release_application (app_t app);
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
int app_write_learn_status (APP app, CTRL ctrl); int app_write_learn_status (app_t app, ctrl_t ctrl);
int app_getattr (APP app, CTRL ctrl, const char *name); int app_readcert (app_t app, const char *certid,
int app_setattr (APP app, const char *name, 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 **), int (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *value, size_t valuelen); const unsigned char *value, size_t valuelen);
int app_sign (APP app, const char *keyidstr, int hashalgo, int app_sign (app_t app, const char *keyidstr, int hashalgo,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); unsigned char **outdata, size_t *outdatalen );
int app_auth (APP app, const char *keyidstr, int app_auth (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
int app_decipher (APP app, const char *keyidstr, int app_decipher (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); unsigned char **outdata, size_t *outdatalen );
int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, int app_genkey (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer); int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer);
int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, int app_change_pin (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
int app_check_pin (APP app, const char *keyidstr, int app_check_pin (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
/*-- app-openpgp.c --*/ /*-- app-openpgp.c --*/
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); int app_select_openpgp (app_t app);
int app_openpgp_cardinfo (APP app, int app_openpgp_cardinfo (app_t app,
char **serialno, char **serialno,
char **disp_name, char **disp_name,
char **pubkey_url, char **pubkey_url,
unsigned char **fpr1, unsigned char **fpr1,
unsigned char **fpr2, unsigned char **fpr2,
unsigned char **fpr3); unsigned char **fpr3);
int app_openpgp_storekey (APP app, int keyno, int app_openpgp_storekey (app_t app, int keyno,
unsigned char *template, size_t template_len, unsigned char *template, size_t template_len,
time_t created_at, time_t created_at,
const unsigned char *m, size_t mlen, const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen, const unsigned char *e, size_t elen,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
int app_openpgp_readkey (APP app, int keyno, int app_openpgp_readkey (app_t app, int keyno,
unsigned char **m, size_t *mlen, unsigned char **m, size_t *mlen,
unsigned char **e, size_t *elen); unsigned char **e, size_t *elen);
/*-- app-nks.c --*/
int app_select_nks (app_t app);
/*-- app-dinsig.c --*/
int app_select_dinsig (app_t app);
#endif #endif

View File

@ -1,5 +1,5 @@
/* app-openpgp.c - The OpenPGP card application. /* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -42,7 +42,7 @@
#include "iso7816.h" #include "iso7816.h"
#include "app-common.h" #include "app-common.h"
#include "tlv.h"
static struct { static struct {
@ -50,121 +50,196 @@ static struct {
int constructed; int constructed;
int get_from; /* Constructed DO with this DO or 0 for direct access. */ int get_from; /* Constructed DO with this DO or 0 for direct access. */
int binary; int binary;
int dont_cache;
int flush_on_error;
char *desc; char *desc;
} data_objects[] = { } data_objects[] = {
{ 0x005E, 0, 0, 1, "Login Data" }, { 0x005E, 0, 0, 1, 0, 0, "Login Data" },
{ 0x5F50, 0, 0, 0, "URL" }, { 0x5F50, 0, 0, 0, 0, 0, "URL" },
{ 0x0065, 1, 0, 1, "Cardholder Related Data"}, { 0x0065, 1, 0, 1, 0, 0, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, "Name" }, { 0x005B, 0, 0x65, 0, 0, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, "Language preferences" }, { 0x5F2D, 0, 0x65, 0, 0, 0, "Language preferences" },
{ 0x5F35, 0, 0x65, 0, "Sex" }, { 0x5F35, 0, 0x65, 0, 0, 0, "Sex" },
{ 0x006E, 1, 0, 1, "Application Related Data" }, { 0x006E, 1, 0, 1, 0, 0, "Application Related Data" },
{ 0x004F, 0, 0x6E, 1, "AID" }, { 0x004F, 0, 0x6E, 1, 0, 0, "AID" },
{ 0x0073, 1, 0, 1, "Discretionary Data Objects" }, { 0x0073, 1, 0, 1, 0, 0, "Discretionary Data Objects" },
{ 0x0047, 0, 0x6E, 1, "Card Capabilities" }, { 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" },
{ 0x00C0, 0, 0x6E, 1, "Extended Card Capabilities" }, { 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" },
{ 0x00C1, 0, 0x6E, 1, "Algorithm Attributes Signature" }, { 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" },
{ 0x00C2, 0, 0x6E, 1, "Algorithm Attributes Decryption" }, { 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" },
{ 0x00C3, 0, 0x6E, 1, "Algorithm Attributes Authentication" }, { 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" },
{ 0x00C4, 0, 0x6E, 1, "CHV Status Bytes" }, { 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" },
{ 0x00C5, 0, 0x6E, 1, "Fingerprints" }, { 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" },
{ 0x00C6, 0, 0x6E, 1, "CA Fingerprints" }, { 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" },
{ 0x007A, 1, 0, 1, "Security Support Template" }, { 0x007A, 1, 0, 1, 0, 0, "Security Support Template" },
{ 0x0093, 0, 0x7A, 1, "Digital Signature Counter" }, { 0x0093, 0, 0x7A, 1, 1, 0, "Digital Signature Counter" },
{ 0 } { 0 }
}; };
struct cache_s {
struct cache_s *next;
int tag;
size_t length;
unsigned char data[1];
};
struct app_local_s {
struct cache_s *cache;
};
static unsigned long convert_sig_counter_value (const unsigned char *value, static unsigned long convert_sig_counter_value (const unsigned char *value,
size_t valuelen); size_t valuelen);
static unsigned long get_sig_counter (APP app); static unsigned long get_sig_counter (APP app);
/* Deconstructor. */
/* Locate a TLV encoded data object in BUFFER of LENGTH and static void
return a pointer to value as well as its length in NBYTES. Return do_deinit (app_t app)
NULL if it was not found. Note, that the function does not check
whether the value fits into the provided buffer.
FIXME: Move this to an extra file, it is mostly duplicated from card.c.
*/
static const unsigned char *
find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
{ {
const unsigned char *s = buffer; if (app && app->app_local)
size_t n = length;
size_t len;
int this_tag;
int composite;
for (;;)
{ {
buffer = s; struct cache_s *c, *c2;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if (!*s || *s == 0xff)
{ /* Skip optional filler between TLV objects. */
s++;
n--;
continue;
}
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
s++;
n--;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if ((*s & 0x1f) == 0x1f)
return NULL; /* We support only up to 2 bytes. */
this_tag = (s[-1] << 8) | (s[0] & 0x7f);
}
else
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
if (len < 0x80)
;
else if (len == 0x81)
{ /* One byte length follows. */
if (!n)
return NULL; /* we expected 1 more bytes with the length. */
len = s[0];
s++; n--;
}
else if (len == 0x82)
{ /* Two byte length follows. */
if (n < 2)
return NULL; /* we expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
else
return NULL; /* APDU limit is 65535, thus it does not make
sense to assume longer length fields. */
if (composite && nestlevel < 100) for (c = app->app_local->cache; c; c = c2)
{ /* Dive into this composite DO after checking for too deep
nesting. */
const unsigned char *tmp_s;
size_t tmp_len;
tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1);
if (tmp_s)
{
*nbytes = tmp_len;
return tmp_s;
}
}
if (this_tag == tag)
{ {
*nbytes = len; c2 = c->next;
return s; xfree (c);
} }
if (len > n) xfree (app->app_local);
return NULL; /* buffer too short to skip to the next tag. */ app->app_local = NULL;
s += len; n -= len; }
}
/* Wrapper around iso7816_get_data which first tries to get the data
from the cache. */
static gpg_error_t
get_cached_data (app_t app, int tag,
unsigned char **result, size_t *resultlen)
{
gpg_error_t err;
int i;
unsigned char *p;
size_t len;
struct cache_s *c;
*result = NULL;
*resultlen = 0;
if (app->app_local)
{
for (c=app->app_local->cache; c; c = c->next)
if (c->tag == tag)
{
p = xtrymalloc (c->length);
if (!p)
return gpg_error (gpg_err_code_from_errno (errno));
memcpy (p, c->data, c->length);
*resultlen = c->length;
*result = p;
return 0;
}
}
err = iso7816_get_data (app->slot, tag, &p, &len);
if (err)
return err;
*result = p;
*resultlen = len;
/* Check whether we should cache this object. */
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag)
{
if (data_objects[i].dont_cache)
return 0;
break;
}
/* No, cache it. */
if (!app->app_local)
app->app_local = xtrycalloc (1, sizeof *app->app_local);
/* Note that we can safely ignore out of core errors. */
if (app->app_local)
{
for (c=app->app_local->cache; c; c = c->next)
assert (c->tag != tag);
c = xtrymalloc (sizeof *c + len);
if (c)
{
memcpy (c->data, p, len);
c->length = len;
c->tag = tag;
c->next = app->app_local->cache;
app->app_local->cache = c;
}
}
return 0;
}
/* Remove DO at TAG from the cache. */
static void
flush_cache_item (app_t app, int tag)
{
struct cache_s *c, *cprev;
int i;
if (!app->app_local)
return;
for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next)
if (c->tag == tag)
{
if (cprev)
cprev->next = c->next;
else
app->app_local->cache = c->next;
xfree (c);
for (c=app->app_local->cache; c ; c = c->next)
assert (c->tag != tag); /* Oops: duplicated entry. */
return;
}
/* Try again if we have an outer tag. */
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag && data_objects[i].get_from
&& data_objects[i].get_from != tag)
flush_cache_item (app, data_objects[i].get_from);
}
/* Flush all entries from the cache which might be out of sync after
an error. */
static void
flush_cache_after_error (app_t app)
{
int i;
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].flush_on_error)
flush_cache_item (app, data_objects[i].tag);
}
/* Flush the entire cache. */
static void
flush_cache (app_t app)
{
if (app && app->app_local)
{
struct cache_s *c, *c2;
for (c = app->app_local->cache; c; c = c2)
{
c2 = c->next;
xfree (c);
}
app->app_local->cache = NULL;
} }
} }
@ -174,7 +249,7 @@ find_tlv (const unsigned char *buffer, size_t length,
NULL if not found or a pointer which must be used to release the NULL if not found or a pointer which must be used to release the
buffer holding value. */ buffer holding value. */
static void * static void *
get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
{ {
int rc, i; int rc, i;
unsigned char *buffer; unsigned char *buffer;
@ -191,13 +266,13 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
rc = -1; rc = -1;
if (data_objects[i].tag && data_objects[i].get_from) if (data_objects[i].tag && data_objects[i].get_from)
{ {
rc = iso7816_get_data (slot, data_objects[i].get_from, rc = get_cached_data (app, data_objects[i].get_from,
&buffer, &buflen); &buffer, &buflen);
if (!rc) if (!rc)
{ {
const unsigned char *s; const unsigned char *s;
s = find_tlv (buffer, buflen, tag, &valuelen, 0); s = find_tlv (buffer, buflen, tag, &valuelen);
if (!s) if (!s)
value = NULL; /* not found */ value = NULL; /* not found */
else if (valuelen > buflen - (s - buffer)) else if (valuelen > buflen - (s - buffer))
@ -213,7 +288,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
if (!value) /* Not in a constructed DO, try simple. */ if (!value) /* Not in a constructed DO, try simple. */
{ {
rc = iso7816_get_data (slot, tag, &buffer, &buflen); rc = get_cached_data (app, tag, &buffer, &buflen);
if (!rc) if (!rc)
{ {
value = buffer; value = buffer;
@ -271,7 +346,7 @@ dump_all_do (int slot)
if (j==i || data_objects[i].tag != data_objects[j].get_from) if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue; continue;
value = find_tlv (buffer, buflen, value = find_tlv (buffer, buflen,
data_objects[j].tag, &valuelen, 0); data_objects[j].tag, &valuelen);
if (!value) if (!value)
; /* not found */ ; /* not found */
else if (valuelen > buflen - (value - buffer)) else if (valuelen > buflen - (value - buffer))
@ -333,7 +408,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
n = 6 + 2 + mlen + 2 + elen; n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n); p = buffer = xtrymalloc (3 + n);
if (!buffer) if (!buffer)
return out_of_core (); return gpg_error (gpg_err_code_from_errno (errno));
*p++ = 0x99; /* ctb */ *p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */ *p++ = n >> 8; /* 2 byte length header */
@ -443,7 +518,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
{ {
/* The serial number is very special. We could have used the /* The serial number is very special. We could have used the
AID DO to retrieve it, but we have it already in the app AID DO to retrieve it, but we have it already in the app
context and the stanmp argument is required anyway which we context and the stamp argument is required anyway which we
can't by other means. The AID DO is available anyway but not can't by other means. The AID DO is available anyway but not
hex formatted. */ hex formatted. */
char *serial; char *serial;
@ -462,7 +537,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
return 0; return 0;
} }
relptr = get_one_do (app->slot, table[idx].tag, &value, &valuelen); relptr = get_one_do (app, table[idx].tag, &value, &valuelen);
if (relptr) if (relptr)
{ {
if (table[idx].special == 1) if (table[idx].special == 1)
@ -517,7 +592,7 @@ do_learn_status (APP app, CTRL ctrl)
/* Verify CHV2 if required. Depending on the configuration of the /* Verify CHV2 if required. Depending on the configuration of the
card CHV1 will also be verified. */ card CHV1 will also be verified. */
static int static int
verify_chv2 (APP app, verify_chv2 (app_t app,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -534,11 +609,19 @@ verify_chv2 (APP app,
return rc; return rc;
} }
if (strlen (pinvalue) < 6)
{
log_error ("prassphrase (CHV2) is too short; minimum length is 6\n");
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
if (rc) if (rc)
{ {
log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc)); log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc));
xfree (pinvalue); xfree (pinvalue);
flush_cache_after_error (app);
return rc; return rc;
} }
app->did_chv2 = 1; app->did_chv2 = 1;
@ -552,6 +635,7 @@ verify_chv2 (APP app,
{ {
log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc)); log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc));
xfree (pinvalue); xfree (pinvalue);
flush_cache_after_error (app);
return rc; return rc;
} }
app->did_chv1 = 1; app->did_chv1 = 1;
@ -569,6 +653,12 @@ verify_chv3 (APP app,
{ {
int rc = 0; int rc = 0;
if (!opt.allow_admin)
{
log_info ("access to admin commands is not configured\n");
return gpg_error (GPG_ERR_EACCES);
}
if (!app->did_chv3) if (!app->did_chv3)
{ {
char *pinvalue; char *pinvalue;
@ -580,11 +670,19 @@ verify_chv3 (APP app,
return rc; return rc;
} }
if (strlen (pinvalue) < 6)
{
log_error ("prassphrase (CHV3) is too short; minimum length is 6\n");
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
xfree (pinvalue); xfree (pinvalue);
if (rc) if (rc)
{ {
log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc)); log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc));
flush_cache_after_error (app);
return rc; return rc;
} }
app->did_chv3 = 1; app->did_chv3 = 1;
@ -629,6 +727,10 @@ do_setattr (APP app, const char *name,
if (rc) if (rc)
return rc; return rc;
/* Flush the cache before writing it, so that the next get operation
will reread the data from the card and thus get synced in case of
errors (e.g. data truncated by the card). */
flush_cache_item (app, table[idx].tag);
rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen); rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen);
if (rc) if (rc)
log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc)); log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
@ -715,7 +817,8 @@ do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
pinvalue, strlen (pinvalue)); pinvalue, strlen (pinvalue));
} }
xfree (pinvalue); xfree (pinvalue);
if (rc)
flush_cache_after_error (app);
leave: leave:
return rc; return rc;
@ -746,13 +849,17 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
keyno--; keyno--;
/* We flush the cache to increase the traffic before a key
generation. This _might_ help a card to gather more entropy. */
flush_cache (app);
rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
if (rc) if (rc)
{ {
log_error ("error reading application data\n"); log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL); return gpg_error (GPG_ERR_GENERAL);
} }
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60) if (!fpr || n != 60)
{ {
rc = gpg_error (GPG_ERR_GENERAL); rc = gpg_error (GPG_ERR_GENERAL);
@ -779,6 +886,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
goto leave; goto leave;
xfree (buffer); buffer = NULL; xfree (buffer); buffer = NULL;
#if 1 #if 1
log_info ("please wait while key is being generated ...\n"); log_info ("please wait while key is being generated ...\n");
start_at = time (NULL); start_at = time (NULL);
@ -800,7 +908,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
} }
log_info ("key generation completed (%d seconds)\n", log_info ("key generation completed (%d seconds)\n",
(int)(time (NULL) - start_at)); (int)(time (NULL) - start_at));
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata) if (!keydata)
{ {
rc = gpg_error (GPG_ERR_CARD); rc = gpg_error (GPG_ERR_CARD);
@ -808,7 +916,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
goto leave; goto leave;
} }
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0); m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m) if (!m)
{ {
rc = gpg_error (GPG_ERR_CARD); rc = gpg_error (GPG_ERR_CARD);
@ -818,7 +926,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
/* log_printhex ("RSA n:", m, mlen); */ /* log_printhex ("RSA n:", m, mlen); */
send_key_data (ctrl, "n", m, mlen); send_key_data (ctrl, "n", m, mlen);
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0); e = find_tlv (keydata, keydatalen, 0x0082, &elen);
if (!e) if (!e)
{ {
rc = gpg_error (GPG_ERR_CARD); rc = gpg_error (GPG_ERR_CARD);
@ -869,7 +977,7 @@ get_sig_counter (APP app)
size_t valuelen; size_t valuelen;
unsigned long ul; unsigned long ul;
relptr = get_one_do (app->slot, 0x0093, &value, &valuelen); relptr = get_one_do (app, 0x0093, &value, &valuelen);
if (!relptr) if (!relptr)
return 0; return 0;
ul = convert_sig_counter_value (value, valuelen); ul = convert_sig_counter_value (value, valuelen);
@ -887,13 +995,13 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
assert (keyno >= 1 && keyno <= 3); assert (keyno >= 1 && keyno <= 3);
rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); rc = get_cached_data (app, 0x006E, &buffer, &buflen);
if (rc) if (rc)
{ {
log_error ("error reading application data\n"); log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL); return gpg_error (GPG_ERR_GENERAL);
} }
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60) if (!fpr || n != 60)
{ {
xfree (buffer); xfree (buffer);
@ -1035,11 +1143,19 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
return rc; return rc;
} }
if (strlen (pinvalue) < 6)
{
log_error ("prassphrase (CHV1) is too short; minimum length is 6\n");
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
if (rc) if (rc)
{ {
log_error ("verify CHV1 failed\n"); log_error ("verify CHV1 failed\n");
xfree (pinvalue); xfree (pinvalue);
flush_cache_after_error (app);
return rc; return rc;
} }
app->did_chv1 = 1; app->did_chv1 = 1;
@ -1053,6 +1169,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
{ {
log_error ("verify CHV2 failed\n"); log_error ("verify CHV2 failed\n");
xfree (pinvalue); xfree (pinvalue);
flush_cache_after_error (app);
return rc; return rc;
} }
app->did_chv2 = 1; app->did_chv2 = 1;
@ -1182,7 +1299,8 @@ do_decipher (APP app, const char *keyidstr,
rc = verify_chv2 (app, pincb, pincb_arg); rc = verify_chv2 (app, pincb, pincb_arg);
if (!rc) if (!rc)
rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen); rc = iso7816_decipher (app->slot, indata, indatalen, 0,
outdata, outdatalen);
return rc; return rc;
} }
@ -1241,7 +1359,7 @@ do_check_pin (APP app, const char *keyidstr,
/* Select the OpenPGP application on the card in SLOT. This function /* Select the OpenPGP application on the card in SLOT. This function
must be used before any other OpenPGP application functions. */ must be used before any other OpenPGP application functions. */
int int
app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) app_select_openpgp (APP app)
{ {
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int slot = app->slot; int slot = app->slot;
@ -1253,10 +1371,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
rc = iso7816_select_application (slot, aid, sizeof aid); rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc) if (!rc)
{ {
app->apptype = "OPENPGP";
app->did_chv1 = 0; app->did_chv1 = 0;
app->did_chv2 = 0; app->did_chv2 = 0;
app->did_chv3 = 0; app->did_chv3 = 0;
/* The OpenPGP card returns the serial number as part of the
AID; because we prefer to use OpenPGP serial numbers, we
replace a possibly already set one from a EF.GDO with this
one. Note, that for current OpenPGP cards, no EF.GDO exists
and thus it won't matter at all. */
rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
if (rc) if (rc)
goto leave; goto leave;
@ -1266,17 +1391,14 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
log_printhex ("", buffer, buflen); log_printhex ("", buffer, buflen);
} }
if (sn) app->card_version = buffer[6] << 8;
{ app->card_version |= buffer[7];
*sn = buffer; xfree (app->serialno);
*snlen = buflen; app->serialno = buffer;
app->card_version = buffer[6] << 8; app->serialnolen = buflen;
app->card_version |= buffer[7]; buffer = NULL;
}
else
xfree (buffer);
relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen); relptr = get_one_do (app, 0x00C4, &buffer, &buflen);
if (!relptr) if (!relptr)
{ {
log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n"); log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n");
@ -1288,7 +1410,9 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
if (opt.verbose > 1) if (opt.verbose > 1)
dump_all_do (slot); dump_all_do (slot);
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = NULL;
app->fnc.getattr = do_getattr; app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr; app->fnc.setattr = do_setattr;
app->fnc.genkey = do_genkey; app->fnc.genkey = do_genkey;
@ -1340,7 +1464,7 @@ app_openpgp_cardinfo (APP app,
if (disp_name) if (disp_name)
{ {
*disp_name = NULL; *disp_name = NULL;
relptr = get_one_do (app->slot, 0x005B, &value, &valuelen); relptr = get_one_do (app, 0x005B, &value, &valuelen);
if (relptr) if (relptr)
{ {
*disp_name = make_printable_string (value, valuelen, 0); *disp_name = make_printable_string (value, valuelen, 0);
@ -1351,7 +1475,7 @@ app_openpgp_cardinfo (APP app,
if (pubkey_url) if (pubkey_url)
{ {
*pubkey_url = NULL; *pubkey_url = NULL;
relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen); relptr = get_one_do (app, 0x5F50, &value, &valuelen);
if (relptr) if (relptr)
{ {
*pubkey_url = make_printable_string (value, valuelen, 0); *pubkey_url = make_printable_string (value, valuelen, 0);
@ -1365,7 +1489,7 @@ app_openpgp_cardinfo (APP app,
*fpr2 = NULL; *fpr2 = NULL;
if (fpr3) if (fpr3)
*fpr3 = NULL; *fpr3 = NULL;
relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen); relptr = get_one_do (app, 0x00C5, &value, &valuelen);
if (relptr && valuelen >= 60) if (relptr && valuelen >= 60)
{ {
if (fpr1) if (fpr1)
@ -1471,7 +1595,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave; goto leave;
} }
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata) if (!keydata)
{ {
log_error ("response does not contain the public key data\n"); log_error ("response does not contain the public key data\n");
@ -1479,7 +1603,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave; goto leave;
} }
a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0); a = find_tlv (keydata, keydatalen, 0x0081, &alen);
if (!a) if (!a)
{ {
log_error ("response does not contain the RSA modulus\n"); log_error ("response does not contain the RSA modulus\n");
@ -1490,7 +1614,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
*m = xmalloc (alen); *m = xmalloc (alen);
memcpy (*m, a, alen); memcpy (*m, a, alen);
a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0); a = find_tlv (keydata, keydatalen, 0x0082, &alen);
if (!e) if (!e)
{ {
log_error ("response does not contain the RSA public exponent\n"); log_error ("response does not contain the RSA public exponent\n");

View File

@ -169,8 +169,7 @@ print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
static void static void
print_name (FILE *fp, const char *text, const char *name) print_name (FILE *fp, const char *text, const char *name)
{ {
tty_fprintf (fp, text); tty_fprintf (fp, "%s", text);
/* FIXME: tty_printf_utf8_string2 eats everything after and /* FIXME: tty_printf_utf8_string2 eats everything after and
including an @ - e.g. when printing an url. */ including an @ - e.g. when printing an url. */
@ -192,7 +191,7 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
if (opt.with_colons) if (opt.with_colons)
fprintf (fp, "%s:", tag); fprintf (fp, "%s:", tag);
else else
tty_fprintf (fp, text); tty_fprintf (fp, "%s", text);
if (name && *name) if (name && *name)
{ {
@ -446,6 +445,14 @@ change_name (void)
if (*p == ' ') if (*p == ' ')
*p = '<'; *p = '<';
if (strlen (isoname) > 39 )
{
tty_printf (_("Error: Combined name too long "
"(limit is %d characters).\n"), 39);
xfree (isoname);
return -1;
}
log_debug ("setting Name to `%s'\n", isoname); log_debug ("setting Name to `%s'\n", isoname);
rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) ); rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
if (rc) if (rc)
@ -468,6 +475,14 @@ change_url (void)
trim_spaces (url); trim_spaces (url);
cpr_kill_prompt (); cpr_kill_prompt ();
if (strlen (url) > 254 )
{
tty_printf (_("Error: URL too long "
"(limit is %d characters).\n"), 254);
xfree (url);
return -1;
}
rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) ); rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
if (rc) if (rc)
log_error ("error setting URL: %s\n", gpg_strerror (rc)); log_error ("error setting URL: %s\n", gpg_strerror (rc));
@ -488,6 +503,14 @@ change_login (void)
trim_spaces (data); trim_spaces (data);
cpr_kill_prompt (); cpr_kill_prompt ();
if (strlen (data) > 254 )
{
tty_printf (_("Error: Login data too long "
"(limit is %d characters).\n"), 254);
xfree (data);
return -1;
}
rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) ); rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
if (rc) if (rc)
log_error ("error setting login data: %s\n", gpg_strerror (rc)); log_error ("error setting login data: %s\n", gpg_strerror (rc));

View File

@ -262,7 +262,7 @@ open_card (void)
app = xcalloc (1, sizeof *app); app = xcalloc (1, sizeof *app);
app->slot = slot; app->slot = slot;
rc = app_select_openpgp (app, &app->serialno, &app->serialnolen); rc = app_select_openpgp (app);
if (rc && !opt.batch) if (rc && !opt.batch)
{ {
write_status_text (STATUS_CARDCTRL, "1"); write_status_text (STATUS_CARDCTRL, "1");

View File

@ -63,8 +63,10 @@ struct agent_card_genkey_s {
struct app_ctx_s; struct app_ctx_s;
struct ctrl_ctx_s; struct ctrl_ctx_s;
typedef struct app_ctx_s *APP; typedef struct app_ctx_s *APP; /* deprecated. */
typedef struct ctrl_ctx_s *CTRL; typedef struct app_ctx_s *app_t;
typedef struct ctrl_ctx_s *CTRL; /* deprecated. */
typedef struct ctrl_ctx_s *ctrl_t;
#define GPG_ERR_GENERAL G10ERR_GENERAL #define GPG_ERR_GENERAL G10ERR_GENERAL
@ -86,6 +88,15 @@ typedef struct ctrl_ctx_s *CTRL;
#define GPG_ERR_WRONG_CARD G10ERR_GENERAL #define GPG_ERR_WRONG_CARD G10ERR_GENERAL
#define GPG_ERR_WRONG_SECKEY G10ERR_WRONG_SECKEY #define GPG_ERR_WRONG_SECKEY G10ERR_WRONG_SECKEY
#define GPG_ERR_PIN_NOT_SYNCED G10ERR_GENERAL #define GPG_ERR_PIN_NOT_SYNCED G10ERR_GENERAL
#define GPG_ERR_NOT_FOUND G10ERR_GENERAL
#define GPG_ERR_BUG G10ERR_GENERAL
#define GPG_ERR_NOT_IMPLEMENTED G10ERR_GENERAL
#define GPG_ERR_BAD_BER G10ERR_GENERAL
#define GPG_ERR_EOF (-1)
#define GPG_ERR_EBUSY G10ERR_GENERAL
#define GPG_ERR_ENOENT G10ERR_OPEN_FILE
#define GPG_ERR_EACCES G10ERR_UNSUPPORTED
typedef int gpg_error_t; typedef int gpg_error_t;
typedef int gpg_err_code_t; typedef int gpg_err_code_t;
@ -94,7 +105,7 @@ typedef int gpg_err_code_t;
#define gpg_err_code(n) (n) #define gpg_err_code(n) (n)
#define gpg_strerror(n) g10_errstr ((n)) #define gpg_strerror(n) g10_errstr ((n))
#define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/ #define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/
#define gpg_err_code_from_errno(n) (G10ERR_GENERAL)
/* We are not using it in a library, so we even let xtrymalloc /* We are not using it in a library, so we even let xtrymalloc
abort. Because we won't never return from these malloc functions, abort. Because we won't never return from these malloc functions,

View File

@ -1,5 +1,5 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver /* ccid-driver.c - USB ChipCardInterfaceDevices driver
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* Written by Werner Koch. * Written by Werner Koch.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
@ -87,16 +87,32 @@
#define DRVNAME "ccid-driver: " #define DRVNAME "ccid-driver: "
#ifdef GNUPG_MAJOR_VERSION /* This source is used within GnuPG. */ /* Depending on how this source is used we either define our error
output to go to stderr or to the jnlib based logging functions. We
use the latter when GNUPG_MAJOR_VERSION is defines or when both,
GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined.
*/
#if defined(GNUPG_MAJOR_VERSION) \
|| (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING))
# if GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ #if defined(GNUPG_SCD_MAIN_HEADER)
# include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
# include "options.h" # include "options.h"
# include "util.h" # include "util.h"
# include "memory.h" # include "memory.h"
# include "cardglue.h" # include "cardglue.h"
# else /* This is the modularized GnuPG 1.9 or later. */ # else /* This is the modularized GnuPG 1.9 or later. */
# include "scdaemon.h" # include "scdaemon.h"
# endif #endif
/* Disable all debugging output for now. */
#undef DBG_CARD_IO
#define DBG_CARD_IO 0
/* Define to print information pertaining the T=1 protocol. */
#undef DEBUG_T1
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \ # define DEBUGOUT(t) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t); } while (0) log_debug (DRVNAME t); } while (0)
@ -132,8 +148,6 @@
#endif /* This source not used by scdaemon. */ #endif /* This source not used by scdaemon. */
/* Define to print information pertaining the T=1 protocol. */
#undef DEBUG_T1 1
@ -171,9 +185,15 @@ struct ccid_driver_s {
int seqno; int seqno;
unsigned char t1_ns; unsigned char t1_ns;
unsigned char t1_nr; unsigned char t1_nr;
int nonnull_nad;
int auto_ifsd;
int max_ifsd;
int ifsd;
}; };
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
int use_crc);
static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen); static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno); size_t *nread, int expected_type, int seqno);
@ -207,13 +227,18 @@ set_msg_len (unsigned char *msg, unsigned int length)
Note, that this code is based on the one in lsusb.c of the Note, that this code is based on the one in lsusb.c of the
usb-utils package, I wrote on 2003-09-01. -wk. */ usb-utils package, I wrote on 2003-09-01. -wk. */
static int static int
parse_ccid_descriptor (const unsigned char *buf, size_t buflen) parse_ccid_descriptor (ccid_driver_t handle,
const unsigned char *buf, size_t buflen)
{ {
unsigned int i; unsigned int i;
unsigned int us; unsigned int us;
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0; int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
handle->nonnull_nad = 0;
handle->auto_ifsd = 0;
handle->max_ifsd = 32;
handle->ifsd = 0;
if (buflen < 54 || buf[0] < 54) if (buflen < 54 || buf[0] < 54)
{ {
DEBUGOUT ("CCID device descriptor is too short\n"); DEBUGOUT ("CCID device descriptor is too short\n");
@ -259,6 +284,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
us = convert_le_u32(buf+28); us = convert_le_u32(buf+28);
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
handle->max_ifsd = us;
us = convert_le_u32(buf+32); us = convert_le_u32(buf+32);
DEBUGOUT_1 (" dwSyncProtocols %08X ", us); DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
@ -307,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
if ((us & 0x0100)) if ((us & 0x0100))
DEBUGOUT (" CCID can set ICC in clock stop mode\n"); DEBUGOUT (" CCID can set ICC in clock stop mode\n");
if ((us & 0x0200)) if ((us & 0x0200))
DEBUGOUT (" NAD value other than 0x00 accpeted\n"); {
DEBUGOUT (" NAD value other than 0x00 accepted\n");
handle->nonnull_nad = 1;
}
if ((us & 0x0400)) if ((us & 0x0400))
DEBUGOUT (" Auto IFSD exchange\n"); {
DEBUGOUT (" Auto IFSD exchange\n");
handle->auto_ifsd = 1;
}
if ((us & 0x00010000)) if ((us & 0x00010000))
{ {
@ -376,7 +408,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
that the device is usable for us. Returns 0 on success or an error that the device is usable for us. Returns 0 on success or an error
code. */ code. */
static int static int
read_device_info (struct usb_device *dev) read_device_info (ccid_driver_t handle, struct usb_device *dev)
{ {
int cfg_no; int cfg_no;
@ -401,8 +433,9 @@ read_device_info (struct usb_device *dev)
{ {
if (ifcdesc->extra) if (ifcdesc->extra)
{ {
if (!parse_ccid_descriptor (ifcdesc->extra, if (!parse_ccid_descriptor (handle,
ifcdesc->extralen)) ifcdesc->extra,
ifcdesc->extralen))
return 0; /* okay. we can use it. */ return 0; /* okay. we can use it. */
} }
} }
@ -445,10 +478,22 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
dev->descriptor->idVendor, dev->descriptor->idProduct); dev->descriptor->idVendor, dev->descriptor->idProduct);
if (!readerno) if (!readerno)
{ {
rc = read_device_info (dev); *handle = calloc (1, sizeof **handle);
if (!*handle)
{
DEBUGOUT ("out of memory\n");
rc = -1;
free (*handle);
*handle = NULL;
goto leave;
}
rc = read_device_info (*handle, dev);
if (rc) if (rc)
{ {
DEBUGOUT ("device not supported\n"); DEBUGOUT ("device not supported\n");
free (*handle);
*handle = NULL;
goto leave; goto leave;
} }
@ -456,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc) if (rc)
{ {
DEBUGOUT_1 ("usb_open failed: %d\n", rc); DEBUGOUT_1 ("usb_open failed: %d\n", rc);
free (*handle);
*handle = NULL;
goto leave; goto leave;
} }
@ -466,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc) if (rc)
{ {
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
free (*handle);
*handle = NULL;
goto leave; goto leave;
} }
*handle = calloc (1, sizeof **handle);
if (!*handle)
{
DEBUGOUT ("out of memory\n");
rc = -1;
goto leave;
}
(*handle)->idev = idev; (*handle)->idev = idev;
idev = NULL; idev = NULL;
/* FIXME: Do we need to get the endpoint addresses from the /* FIXME: Do we need to get the endpoint addresses from the
@ -495,7 +537,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
usb_free_match (match); usb_free_match (match);
if (!rc && !*handle) if (!rc && !*handle)
rc = -1; /* In case we didn't enter the while lool at all. */ rc = -1; /* In case we didn't enter the while loop at all. */
return rc; return rc;
} }
@ -579,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
int i, rc; int i, rc;
size_t msglen; size_t msglen;
retry:
rc = usb_bulk_read (handle->idev, rc = usb_bulk_read (handle->idev,
0x82, 0x82,
buffer, length, buffer, length,
@ -615,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
return -1; return -1;
} }
if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
{
/* Card present and active, time extension requested. */
DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
buffer[7], buffer[8]);
goto retry;
}
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n" DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n"
" data:", buffer[7], buffer[8], buffer[9] ); " data:", buffer[7], buffer[8], buffer[9] );
for (i=10; i < msglen; i++) for (i=10; i < msglen; i++)
@ -682,7 +733,7 @@ ccid_poll (ccid_driver_t handle)
int int
ccid_slot_status (ccid_driver_t handle) ccid_slot_status (ccid_driver_t handle, int *statusbits)
{ {
int rc; int rc;
unsigned char msg[100]; unsigned char msg[100];
@ -703,6 +754,7 @@ ccid_slot_status (ccid_driver_t handle)
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno); rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
if (rc) if (rc)
return rc; return rc;
*statusbits = (msg[7] & 3);
return 0; return 0;
} }
@ -714,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle,
{ {
int rc; int rc;
unsigned char msg[100]; unsigned char msg[100];
size_t msglen; unsigned char *tpdu;
size_t msglen, tpdulen;
unsigned char seqno; unsigned char seqno;
int use_crc = 0;
unsigned int edc;
int i;
msg[0] = PC_to_RDR_IccPowerOn; msg[0] = PC_to_RDR_IccPowerOn;
msg[5] = 0; /* slot */ msg[5] = 0; /* slot */
@ -743,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle,
*atrlen = n; *atrlen = n;
} }
/* Setup parameters to select T=1. */
msg[0] = PC_to_RDR_SetParameters;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 1; /* Select T=1. */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
/* FIXME: Get those values from the ATR. */
msg[10]= 0x01; /* Fi/Di */
msg[11]= 0x10; /* LRC, direct convention. */
msg[12]= 0; /* Extra guardtime. */
msg[13]= 0x41; /* BWI/CWI */
msg[14]= 0; /* No clock stoppping. */
msg[15]= 254; /* IFSC */
msg[16]= 0; /* Does not support non default NAD values. */
set_msg_len (msg, 7);
msglen = 10 + 7;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
/* Note that we ignore the error code on purpose. */
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
/* Send an S-Block with our maximun IFSD to the CCID. */
if (!handle->auto_ifsd)
{
tpdu = msg+10;
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
tpdu[2] = 1;
tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
tpdulen = 4;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 0;
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
#ifdef DEBUG_T1
fprintf (stderr, "T1: put %c-block seq=%d\n",
((msg[11] & 0xc0) == 0x80)? 'R' :
(msg[11] & 0x80)? 'S' : 'I',
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
#endif
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
/* Fixme: The next line for the current Valgrid without support
for USB IOCTLs. */
memset (msg, 0, sizeof msg);
rc = bulk_in (handle, msg, sizeof msg, &msglen,
RDR_to_PC_DataBlock, seqno);
if (rc)
return rc;
tpdu = msg + 10;
tpdulen = msglen - 10;
if (tpdulen < 4)
{
DEBUGOUT ("cannot yet handle short blocks!\n");
return -1;
}
#ifdef DEBUG_T1
fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
((msg[11] & 0xc0) == 0x80)? 'R' :
(msg[11] & 0x80)? 'S' : 'I',
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
);
#endif
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
{
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
return -1;
}
DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
}
return 0; return 0;
} }
static unsigned int
compute_edc (const unsigned char *data, size_t datalen, int use_crc)
{
if (use_crc)
{
return 0x42; /* Not yet implemented. */
}
else
{
unsigned char crc = 0;
for (; datalen; datalen--)
crc ^= *data++;
return crc;
}
}
/* /*
Protocol T=1 overview Protocol T=1 overview
@ -806,17 +986,19 @@ ccid_transceive (ccid_driver_t handle,
unsigned char *resp, size_t maxresplen, size_t *nresp) unsigned char *resp, size_t maxresplen, size_t *nresp)
{ {
int rc; int rc;
unsigned char send_buffer[10+258], recv_buffer[10+258]; unsigned char send_buffer[10+259], recv_buffer[10+259];
const unsigned char *apdu; const unsigned char *apdu;
size_t apdulen; size_t apdulen;
unsigned char *msg, *tpdu, *p; unsigned char *msg, *tpdu, *p;
size_t msglen, tpdulen, n; size_t msglen, tpdulen, last_tpdulen, n;
unsigned char seqno; unsigned char seqno;
int i; int i;
unsigned char crc; unsigned int edc;
int use_crc = 0;
size_t dummy_nresp; size_t dummy_nresp;
int next_chunk = 1; int next_chunk = 1;
int sending = 1; int sending = 1;
int retries = 0;
if (!nresp) if (!nresp)
nresp = &dummy_nresp; nresp = &dummy_nresp;
@ -839,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle,
return -1; /* Invalid length. */ return -1; /* Invalid length. */
tpdu = msg+10; tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ /* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
if (apdulen > 128 /* fixme: replace by ifsc */) if (apdulen > 128 /* fixme: replace by ifsc */)
{ {
@ -850,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle,
} }
tpdu[2] = apdulen; tpdu[2] = apdulen;
memcpy (tpdu+3, apdu, apdulen); memcpy (tpdu+3, apdu, apdulen);
crc = 0; tpdulen = 3 + apdulen;
for (i=0,p=tpdu; i < apdulen+3; i++) edc = compute_edc (tpdu, tpdulen, use_crc);
crc ^= *p++; if (use_crc)
tpdu[3+apdulen] = crc; tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
tpdulen = apdulen + 4;
} }
msg[0] = PC_to_RDR_XfrBlock; msg[0] = PC_to_RDR_XfrBlock;
@ -866,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle,
msg[9] = 0; /* RFU */ msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen); set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen; msglen = 10 + tpdulen;
last_tpdulen = tpdulen;
DEBUGOUT ("sending"); DEBUGOUT ("sending");
for (i=0; i < msglen; i++) for (i=0; i < msglen; i++)
@ -913,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle,
if (!(tpdu[1] & 0x80)) if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */ { /* This is an I-block. */
retries = 0;
if (sending) if (sending)
{ /* last block sent was successful. */ { /* last block sent was successful. */
handle->t1_ns ^= 1; handle->t1_ns ^= 1;
@ -924,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle,
{ /* Reponse does not match our sequence number. */ { /* Reponse does not match our sequence number. */
msg = send_buffer; msg = send_buffer;
tpdu = msg+10; tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ /* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
tpdu[2] = 0; tpdu[2] = 0;
tpdulen = 3; tpdulen = 3;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++) edc = compute_edc (tpdu, tpdulen, use_crc);
crc ^= *p++; if (use_crc)
tpdu[tpdulen++] = crc; tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
continue; continue;
} }
@ -944,7 +1129,9 @@ ccid_transceive (ccid_driver_t handle,
{ {
if (n > maxresplen) if (n > maxresplen)
{ {
DEBUGOUT ("provided buffer too short for received data\n"); DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n",
(unsigned int)n, (unsigned int)maxresplen);
return -1; return -1;
} }
@ -959,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle,
msg = send_buffer; msg = send_buffer;
tpdu = msg+10; tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ /* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
tpdu[2] = 0; tpdu[2] = 0;
tpdulen = 3; tpdulen = 3;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++) edc = compute_edc (tpdu, tpdulen, use_crc);
crc ^= *p++; if (use_crc)
tpdu[tpdulen++] = crc; tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
} }
else if ((tpdu[1] & 0xc0) == 0x80) else if ((tpdu[1] & 0xc0) == 0x80)
{ /* This is a R-block. */ { /* This is a R-block. */
if ( (tpdu[1] & 0x0f)) if ( (tpdu[1] & 0x0f))
{ /* Error: repeat last block */ { /* Error: repeat last block */
if (++retries > 3)
{
DEBUGOUT ("3 failed retries\n");
return -1;
}
msg = send_buffer; msg = send_buffer;
tpdulen = last_tpdulen;
} }
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns) else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
{ /* Reponse does not match our sequence number. */ { /* Reponse does not match our sequence number. */
@ -981,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle,
} }
else if (sending) else if (sending)
{ /* Send next chunk. */ { /* Send next chunk. */
retries = 0;
msg = send_buffer; msg = send_buffer;
next_chunk = 1; next_chunk = 1;
handle->t1_ns ^= 1; handle->t1_ns ^= 1;
@ -993,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle,
} }
else else
{ /* This is a S-block. */ { /* This is a S-block. */
retries = 0;
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n", DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f)); (tpdu[1] & 0x1f));
@ -1001,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle,
unsigned char bwi = tpdu[3]; unsigned char bwi = tpdu[3];
msg = send_buffer; msg = send_buffer;
tpdu = msg+10; tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ /* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */ tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
tpdu[2] = 1; tpdu[2] = 1;
tpdu[3] = bwi; tpdu[3] = bwi;
tpdulen = 4; tpdulen = 4;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++) edc = compute_edc (tpdu, tpdulen, use_crc);
crc ^= *p++; if (use_crc)
tpdu[tpdulen++] = crc; tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi); DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
} }
else else
@ -1028,6 +1226,7 @@ main (int argc, char **argv)
{ {
int rc; int rc;
ccid_driver_t ccid; ccid_driver_t ccid;
unsigned int slotstat;
rc = ccid_open_reader (&ccid, 0); rc = ccid_open_reader (&ccid, 0);
if (rc) if (rc)
@ -1041,7 +1240,7 @@ main (int argc, char **argv)
ccid_poll (ccid); ccid_poll (ccid);
fputs ("getting slot status ...\n", stderr); fputs ("getting slot status ...\n", stderr);
rc = ccid_slot_status (ccid); rc = ccid_slot_status (ccid, &slotstat);
if (rc) if (rc)
return 1; return 1;

View File

@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno);
int ccid_close_reader (ccid_driver_t handle); int ccid_close_reader (ccid_driver_t handle);
int ccid_get_atr (ccid_driver_t handle, int ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen); unsigned char *atr, size_t maxatrlen, size_t *atrlen);
int ccid_slot_status (ccid_driver_t handle, int *statusbits);
int ccid_transceive (ccid_driver_t handle, int ccid_transceive (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen, const unsigned char *apdu, size_t apdulen,
unsigned char *resp, size_t maxresplen, size_t *nresp); unsigned char *resp, size_t maxresplen, size_t *nresp);

View File

@ -341,6 +341,8 @@ enum cmd_and_opt_values
octapiDriver, octapiDriver,
opcscDriver, opcscDriver,
oDisableCCID, oDisableCCID,
oAllowAdmin,
oDenyAdmin,
aTest aTest
}; };
@ -521,6 +523,10 @@ static ARGPARSE_OPTS opts[] = {
{ oSetNotation, "notation-data", 2, "@" }, /* Alias */ { oSetNotation, "notation-data", 2, "@" }, /* Alias */
{ oSigNotation, "sig-notation", 2, "@" }, { oSigNotation, "sig-notation", 2, "@" },
{ oCertNotation, "cert-notation", 2, "@" }, { oCertNotation, "cert-notation", 2, "@" },
#ifdef ENABLE_CARD_SUPPORT
{ oAllowAdmin, "allow-admin",0,N_("allow the use of admin card commands")},
{ oDenyAdmin, "deny-admin",0,"@"},
#endif
{ 302, NULL, 0, N_( { 302, NULL, 0, N_(
"@\n(See the man page for a complete listing of all commands and options)\n" "@\n(See the man page for a complete listing of all commands and options)\n"
@ -1699,6 +1705,8 @@ main( int argc, char **argv )
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break; case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
case oDisableCCID: opt.disable_ccid = 1; break; case oDisableCCID: opt.disable_ccid = 1; break;
case oAllowAdmin: opt.allow_admin = 1; break;
case oDenyAdmin: opt.allow_admin = 0; break;
#endif /* ENABLE_CARD_SUPPORT*/ #endif /* ENABLE_CARD_SUPPORT*/
case oArmor: opt.armor = 1; opt.no_armor=0; break; case oArmor: opt.armor = 1; opt.no_armor=0; break;

View File

@ -1,5 +1,5 @@
/* iso7816.c - ISO 7816 commands /* iso7816.c - ISO 7816 commands
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -24,7 +24,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#if GNUPG_MAJOR_VERSION == 1 #if defined(GNUPG_SCD_MAIN_HEADER)
#include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source /* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over copied from the current GnuPG >= 1.9 and is maintained over
there. */ there. */
@ -47,10 +49,13 @@
#define CMD_RESET_RETRY_COUNTER 0x2C #define CMD_RESET_RETRY_COUNTER 0x2C
#define CMD_GET_DATA 0xCA #define CMD_GET_DATA 0xCA
#define CMD_PUT_DATA 0xDA #define CMD_PUT_DATA 0xDA
#define CMD_MSE 0x22
#define CMD_PSO 0x2A #define CMD_PSO 0x2A
#define CMD_INTERNAL_AUTHENTICATE 0x88 #define CMD_INTERNAL_AUTHENTICATE 0x88
#define CMD_GENERATE_KEYPAIR 0x47 #define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84 #define CMD_GET_CHALLENGE 0x84
#define CMD_READ_BINARY 0xB0
#define CMD_READ_RECORD 0xB2
static gpg_error_t static gpg_error_t
map_sw (int sw) map_sw (int sw)
@ -66,6 +71,8 @@ map_sw (int sw)
case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break; case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break;
case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break; case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break; case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break; case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
@ -75,6 +82,9 @@ map_sw (int sw)
case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break; case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break; case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break; case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break;
default: default:
if ((sw & 0x010000)) if ((sw & 0x010000))
ec = GPG_ERR_GENERAL; /* Should not happen. */ ec = GPG_ERR_GENERAL; /* Should not happen. */
@ -91,18 +101,79 @@ map_sw (int sw)
apdu_open_reader (), AID is a buffer of size AIDLEN holding the apdu_open_reader (), AID is a buffer of size AIDLEN holding the
requested application ID. The function can't be used to enumerate requested application ID. The function can't be used to enumerate
AIDs and won't return the AID on success. The return value is 0 AIDs and won't return the AID on success. The return value is 0
for okay or GNUPG error code. Note that ISO error codes are for okay or a GPG error code. Note that ISO error codes are
internally mapped. */ internally mapped. */
gpg_error_t gpg_error_t
iso7816_select_application (int slot, const char *aid, size_t aidlen) iso7816_select_application (int slot, const char *aid, size_t aidlen)
{ {
static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int sw; int sw;
int p1 = 0x0C; /* No FCI to be returned. */
if (aidlen == sizeof openpgp_aid
&& !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
p1 = 0; /* The current openpgp cards don't allow 0x0c. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
return map_sw (sw); return map_sw (sw);
} }
gpg_error_t
iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen)
{
int sw, p0, p1;
unsigned char tagbuf[2];
tagbuf[0] = (tag >> 8) & 0xff;
tagbuf[1] = tag & 0xff;
if (result || resultlen)
{
*result = NULL;
*resultlen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
else
{
p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
p0, p1, 2, tagbuf );
return map_sw (sw);
}
return 0;
}
/* This is a private command currently only working for TCOS cards. */
gpg_error_t
iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
}
return map_sw (sw);
}
/* Perform a VERIFY command on SLOT using the card holder verification /* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
gpg_error_t gpg_error_t
@ -134,7 +205,7 @@ iso7816_change_reference_data (int slot, int chvno,
buf = xtrymalloc (oldchvlen + newchvlen); buf = xtrymalloc (oldchvlen + newchvlen);
if (!buf) if (!buf)
return out_of_core (); return gpg_error (gpg_err_code_from_errno (errno));
if (oldchvlen) if (oldchvlen)
memcpy (buf, oldchv, oldchvlen); memcpy (buf, oldchv, oldchvlen);
memcpy (buf+oldchvlen, newchv, newchvlen); memcpy (buf+oldchvlen, newchv, newchvlen);
@ -205,6 +276,23 @@ iso7816_put_data (int slot, int tag,
return map_sw (sw); return map_sw (sw);
} }
/* Manage Security Environment. This is a weird operation and there
is no easy abstraction for it. Furthermore, some card seem to have
a different interpreation of 7816-8 and thus we resort to let the
caller decide what to do. */
gpg_error_t
iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data, size_t datalen)
{
int sw;
if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen)
return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, datalen, data);
return map_sw (sw);
}
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On /* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
success 0 is returned and the data is availavle in a newly success 0 is returned and the data is availavle in a newly
@ -236,13 +324,14 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
} }
/* Perform the security operation DECIPHER. On /* Perform the security operation DECIPHER. PADIND is the padding
success 0 is returned and the plaintext is available in a newly indicator to be used. It should be 0 if no padding is required, a
allocated buffer stored at RESULT with its length stored at value of -1 suppresses the padding byte. On success 0 is returned
RESULTLEN. */ and the plaintext is available in a newly allocated buffer stored
at RESULT with its length stored at RESULTLEN. */
gpg_error_t gpg_error_t
iso7816_decipher (int slot, const unsigned char *data, size_t datalen, iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) int padind, unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
unsigned char *buf; unsigned char *buf;
@ -252,15 +341,24 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
/* We need to prepend the padding indicator. */ if (padind >= 0)
buf = xtrymalloc (datalen + 1); {
if (!buf) /* We need to prepend the padding indicator. */
return out_of_core (); buf = xtrymalloc (datalen + 1);
*buf = 0; /* Padding indicator. */ if (!buf)
memcpy (buf+1, data, datalen); return gpg_error (gpg_err_code_from_errno (errno));
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
result, resultlen); *buf = padind; /* Padding indicator. */
xfree (buf); memcpy (buf+1, data, datalen);
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
result, resultlen);
xfree (buf);
}
else
{
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen, data,
result, resultlen);
}
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
/* Make sure that pending buffers are released. */ /* Make sure that pending buffers are released. */
@ -381,3 +479,139 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
return 0; return 0;
} }
/* Perform a READ BINARY command requesting a maximum of NMAX bytes
from OFFSET. With NMAX = 0 the entire file is read. The result is
stored in a newly allocated buffer at the address passed by RESULT.
Returns the length of this data at the address of RESULTLEN. */
gpg_error_t
iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buffer;
size_t bufferlen;
int read_all = !nmax;
size_t n;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
if (offset > 32767)
return gpg_error (GPG_ERR_INV_VALUE);
do
{
buffer = NULL;
bufferlen = 0;
/* Fixme: Either the ccid driver or the TCOS cards have problems
with an Le of 0. */
if (read_all || nmax > 254)
n = 254;
else
n = nmax;
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{
/* Make sure that pending buffers are released. */
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return map_sw (sw);
}
if (*result) /* Need to extend the buffer. */
{
unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
if (!p)
{
gpg_error_t err = gpg_error_from_errno (errno);
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return err;
}
*result = p;
memcpy (*result + *resultlen, buffer, bufferlen);
*resultlen += bufferlen;
xfree (buffer);
buffer = NULL;
}
else /* Transfer the buffer into our result. */
{
*result = buffer;
*resultlen = bufferlen;
}
offset += bufferlen;
if (offset > 32767)
break; /* We simply truncate the result for too large
files. */
if (nmax > bufferlen)
nmax -= bufferlen;
else
nmax = 0;
}
while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
return 0;
}
/* Perform a READ RECORD command. RECNO gives the record number to
read with 0 indicating the current record. RECCOUNT must be 1 (not
all cards support reading of more than one record). SHORT_EF
should be 0 to read the current EF or contain a short EF. The
result is stored in a newly allocated buffer at the address passed
by RESULT. Returns the length of this data at the address of
RESULTLEN. */
gpg_error_t
iso7816_read_record (int slot, int recno, int reccount, int short_ef,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buffer;
size_t bufferlen;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
if (recno < 0 || recno > 255 || reccount != 1
|| short_ef < 0 || short_ef > 254 )
return gpg_error (GPG_ERR_INV_VALUE);
buffer = NULL;
bufferlen = 0;
/* Fixme: Either the ccid driver of the TCOS cards have problems
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
recno,
short_ef? short_ef : 0x04,
-1, NULL,
254, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{
/* Make sure that pending buffers are released. */
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return map_sw (sw);
}
*result = buffer;
*resultlen = bufferlen;
return 0;
}

View File

@ -27,6 +27,10 @@
gpg_error_t iso7816_select_application (int slot, gpg_error_t iso7816_select_application (int slot,
const char *aid, size_t aidlen); const char *aid, size_t aidlen);
gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_verify (int slot, gpg_error_t iso7816_verify (int slot,
int chvno, const char *chv, size_t chvlen); int chvno, const char *chv, size_t chvlen);
gpg_error_t iso7816_change_reference_data (int slot, int chvno, gpg_error_t iso7816_change_reference_data (int slot, int chvno,
@ -38,11 +42,15 @@ gpg_error_t iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_put_data (int slot, int tag, gpg_error_t iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen); const unsigned char *data, size_t datalen);
gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data,
size_t datalen);
gpg_error_t iso7816_compute_ds (int slot, gpg_error_t iso7816_compute_ds (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_decipher (int slot, gpg_error_t iso7816_decipher (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int padind,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_internal_authenticate (int slot, gpg_error_t iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
@ -56,5 +64,10 @@ gpg_error_t iso7816_read_public_key (int slot,
gpg_error_t iso7816_get_challenge (int slot, gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer); int length, unsigned char *buffer);
gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
int short_ef,
unsigned char **result, size_t *resultlen);
#endif /*ISO7816_H*/ #endif /*ISO7816_H*/

View File

@ -195,6 +195,7 @@ struct
const char *ctapi_driver; /* Library to access the ctAPI. */ const char *ctapi_driver; /* Library to access the ctAPI. */
const char *pcsc_driver; /* Library to access the PC/SC system. */ const char *pcsc_driver; /* Library to access the PC/SC system. */
int disable_ccid; /* Disable the use of the internal CCID driver. */ int disable_ccid; /* Disable the use of the internal CCID driver. */
int allow_admin; /* Allow the use of Admin commands. */
#endif /*ENABLE_CARD_SUPPORT*/ #endif /*ENABLE_CARD_SUPPORT*/
} opt; } opt;

View File

@ -83,6 +83,7 @@ get_status_string ( int no )
case STATUS_ENTER : s = "ENTER"; break; case STATUS_ENTER : s = "ENTER"; break;
case STATUS_LEAVE : s = "LEAVE"; break; case STATUS_LEAVE : s = "LEAVE"; break;
case STATUS_ABORT : s = "ABORT"; break; case STATUS_ABORT : s = "ABORT"; break;
case STATUS_NEWSIG : s = "NEWSIG"; break;
case STATUS_GOODSIG: s = "GOODSIG"; break; case STATUS_GOODSIG: s = "GOODSIG"; break;
case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break; case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break;
case STATUS_KEYREVOKED: s = "KEYREVOKED"; break; case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;

View File

@ -101,6 +101,7 @@
#define STATUS_IMPORT_CHECK 69 #define STATUS_IMPORT_CHECK 69
#define STATUS_REVKEYSIG 70 #define STATUS_REVKEYSIG 70
#define STATUS_CARDCTRL 71 #define STATUS_CARDCTRL 71
#define STATUS_NEWSIG 72
/*-- status.c --*/ /*-- status.c --*/
void set_status_fd ( int fd ); void set_status_fd ( int fd );

209
g10/tlv.c Normal file
View File

@ -0,0 +1,209 @@
/* tlv.c - Tag-Length-Value Utilities
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "errors.h"
#include "util.h"
#include "packet.h"
#include "tlv.h"
static const unsigned char *
do_find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
{
const unsigned char *s = buffer;
size_t n = length;
size_t len;
int this_tag;
int composite;
for (;;)
{
buffer = s;
if (n < 2)
return NULL; /* Buffer definitely too short for tag and length. */
if (!*s || *s == 0xff)
{ /* Skip optional filler between TLV objects. */
s++;
n--;
continue;
}
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
s++;
n--;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if ((*s & 0x1f) == 0x1f)
return NULL; /* We support only up to 2 bytes. */
this_tag = (s[-1] << 8) | (s[0] & 0x7f);
}
else
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
if (len < 0x80)
;
else if (len == 0x81)
{ /* One byte length follows. */
if (!n)
return NULL; /* we expected 1 more bytes with the length. */
len = s[0];
s++; n--;
}
else if (len == 0x82)
{ /* Two byte length follows. */
if (n < 2)
return NULL; /* We expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
else
return NULL; /* APDU limit is 65535, thus it does not make
sense to assume longer length fields. */
if (composite && nestlevel < 100)
{ /* Dive into this composite DO after checking for a too deep
nesting. */
const unsigned char *tmp_s;
size_t tmp_len;
tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
if (tmp_s)
{
*nbytes = tmp_len;
return tmp_s;
}
}
if (this_tag == tag)
{
*nbytes = len;
return s;
}
if (len > n)
return NULL; /* Buffer too short to skip to the next tag. */
s += len; n -= len;
}
}
/* 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)
{
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
on success. */
gpg_error_t
parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed, int *r_ndef,
size_t *r_length, size_t *r_nhdr)
{
int c;
unsigned long tag;
const unsigned char *buf = *buffer;
size_t length = *size;
*r_ndef = 0;
*r_length = 0;
*r_nhdr = 0;
/* Get the tag. */
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
*r_class = (c & 0xc0) >> 6;
*r_constructed = !!(c & 0x20);
tag = c & 0x1f;
if (tag == 0x1f)
{
tag = 0;
do
{
tag <<= 7;
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
tag |= c & 0x7f;
}
while (c & 0x80);
}
*r_tag = tag;
/* Get the length. */
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
if ( !(c & 0x80) )
*r_length = c;
else if (c == 0x80)
*r_ndef = 1;
else if (c == 0xff)
return gpg_error (GPG_ERR_BAD_BER);
else
{
unsigned long len = 0;
int count = c & 0x7f;
if (count > sizeof (len) || count > sizeof (size_t))
return gpg_error (GPG_ERR_BAD_BER);
for (; count; count--)
{
len <<= 8;
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
len |= c & 0xff;
}
*r_length = len;
}
/* Without this kludge some example certs can't be parsed. */
if (*r_class == CLASS_UNIVERSAL && !*r_tag)
*r_length = 0;
*buffer = buf;
*size = length;
return 0;
}

86
g10/tlv.h Normal file
View File

@ -0,0 +1,86 @@
/* tlv.h - Tag-Length-Value Utilities
* Copyright (C) 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifndef SCD_TLV_H
#define SCD_TLV_H 1
#include "types.h"
#include "cardglue.h"
enum tlv_tag_class {
CLASS_UNIVERSAL = 0,
CLASS_APPLICATION = 1,
CLASS_CONTEXT = 2,
CLASS_PRIVATE =3
};
enum tlv_tag_type {
TAG_NONE = 0,
TAG_BOOLEAN = 1,
TAG_INTEGER = 2,
TAG_BIT_STRING = 3,
TAG_OCTET_STRING = 4,
TAG_NULL = 5,
TAG_OBJECT_ID = 6,
TAG_OBJECT_DESCRIPTOR = 7,
TAG_EXTERNAL = 8,
TAG_REAL = 9,
TAG_ENUMERATED = 10,
TAG_EMBEDDED_PDV = 11,
TAG_UTF8_STRING = 12,
TAG_REALTIVE_OID = 13,
TAG_SEQUENCE = 16,
TAG_SET = 17,
TAG_NUMERIC_STRING = 18,
TAG_PRINTABLE_STRING = 19,
TAG_TELETEX_STRING = 20,
TAG_VIDEOTEX_STRING = 21,
TAG_IA5_STRING = 22,
TAG_UTC_TIME = 23,
TAG_GENERALIZED_TIME = 24,
TAG_GRAPHIC_STRING = 25,
TAG_VISIBLE_STRING = 26,
TAG_GENERAL_STRING = 27,
TAG_UNIVERSAL_STRING = 28,
TAG_CHARACTER_STRING = 29,
TAG_BMP_STRING = 30
};
/* 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);
/* 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
on success. */
gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed,
int *r_ndef, size_t *r_length, size_t *r_nhdr);
#endif /* SCD_TLV_H */