aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog-20114
-rw-r--r--scd/Makefile.am1
-rw-r--r--scd/apdu.c252
-rw-r--r--scd/apdu.h4
-rw-r--r--scd/app-common.h3
-rw-r--r--scd/app.c104
-rw-r--r--scd/atr.c252
-rw-r--r--scd/atr.h2
-rw-r--r--scd/command.c290
-rw-r--r--scd/scdaemon.c9
-rw-r--r--scd/scdaemon.h7
11 files changed, 510 insertions, 418 deletions
diff --git a/scd/ChangeLog-2011 b/scd/ChangeLog-2011
index 5a87a4c79..9184af4c5 100644
--- a/scd/ChangeLog-2011
+++ b/scd/ChangeLog-2011
@@ -87,10 +87,6 @@
(handle_control): New.
(main): Handle the case 6 of handle_control.
-2011-10-13 Marcus Brinkmann <[email protected]>
-
- * Makefile.am, apdu.c, app.c, command.c, scdaemon.c: Port to Npth.
-
2011-08-10 Werner Koch <[email protected]>
* command.c (cmd_killscd): Use the new assuan force close flag
diff --git a/scd/Makefile.am b/scd/Makefile.am
index d9a3ddbde..09aea0e84 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -37,6 +37,7 @@ card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c
scdaemon_SOURCES = \
scdaemon.c scdaemon.h \
command.c \
+ atr.c atr.h \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
diff --git a/scd/apdu.c b/scd/apdu.c
index e4ebc3586..e914f7b24 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -465,8 +465,11 @@ apdu_strerror (int rc)
case SW_FILE_NOT_FOUND : return "file not found";
case SW_RECORD_NOT_FOUND:return "record not found";
case SW_REF_NOT_FOUND : return "reference not found";
- case SW_BAD_LC : return "bad Lc";
- case SW_BAD_P0_P1 : return "bad P0 or P1";
+ case SW_NOT_ENOUGH_MEMORY: return "not enough memory space in the file";
+ case SW_INCONSISTENT_LC: return "Lc inconsistent with TLV structure.";
+ case SW_INCORRECT_P0_P1: return "incorrect parameters P0,P1";
+ case SW_BAD_LC : return "Lc inconsistent with P0,P1";
+ case SW_BAD_P0_P1 : return "bad P0,P1";
case SW_INS_NOT_SUP : return "instruction not supported";
case SW_CLA_NOT_SUP : return "class not supported";
case SW_SUCCESS : return "success";
@@ -2052,7 +2055,7 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
{
int sw;
unsigned char *pin_verify;
- unsigned long len = PIN_VERIFY_STRUCTURE_SIZE;
+ int len = PIN_VERIFY_STRUCTURE_SIZE;
unsigned char result[2];
size_t resultlen = 2;
@@ -2108,12 +2111,23 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
pin_verify[22] = p1; /* abData[3] */
pin_verify[23] = 0x00; /* abData[4] */
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, pininfo->maxlen);
+
sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
pin_verify, len, result, &resultlen);
xfree (pin_verify);
if (sw || resultlen < 2)
return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
return sw;
}
@@ -2125,7 +2139,7 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
{
int sw;
unsigned char *pin_modify;
- unsigned long len = PIN_MODIFY_STRUCTURE_SIZE;
+ int len = PIN_MODIFY_STRUCTURE_SIZE;
unsigned char result[2];
size_t resultlen = 2;
@@ -2192,12 +2206,21 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
pin_modify[27] = p1; /* abData[3] */
pin_modify[28] = 0x00; /* abData[4] */
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, (int)pininfo->maxlen);
+
sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
pin_modify, len, result, &resultlen);
xfree (pin_modify);
if (sw || resultlen < 2)
- return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
return sw;
}
@@ -2783,6 +2806,9 @@ apdu_open_reader (const char *portstr, int *r_no_service)
static int pcsc_api_loaded, ct_api_loaded;
int slot;
+ if (DBG_READER)
+ log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
+
if (r_no_service)
*r_no_service = 0;
@@ -2797,6 +2823,8 @@ apdu_open_reader (const char *portstr, int *r_no_service)
if (slot != -1)
{
once_available = 1;
+ if (DBG_READER)
+ log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
return slot; /* got one */
}
@@ -2807,14 +2835,22 @@ apdu_open_reader (const char *portstr, int *r_no_service)
and over again. To reset this flag "gpgconf --kill scdaemon"
can be used. */
if (once_available)
- return -1;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_open_reader => slot=-1 (once_avail)\n");
+ return -1;
+ }
/* If a CCID reader specification has been given, the user does
not want a fallback to other drivers. */
if (portstr)
for (s=portstr, i=0; *s; s++)
if (*s == ':' && (++i == 3))
- return -1;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
+ return -1;
+ }
}
#endif /* HAVE_LIBUSB */
@@ -2939,6 +2975,8 @@ apdu_open_reader (const char *portstr, int *r_no_service)
if (slot == -1 && r_no_service && pcsc_no_service)
*r_no_service = 1;
+ if (DBG_READER)
+ log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot);
return slot;
}
@@ -2993,13 +3031,31 @@ apdu_close_reader (int slot)
{
int sw;
+ if (DBG_READER)
+ log_debug ("enter: apdu_close_reader: slot=%d\n", slot);
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return SW_HOST_NO_DRIVER;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
sw = apdu_disconnect (slot);
if (sw)
- return sw;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
+ return sw;
+ }
if (reader_table[slot].close_reader)
- return reader_table[slot].close_reader (slot);
+ {
+ sw = reader_table[slot].close_reader (slot);
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
+ return sw;
+ }
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
return SW_HOST_NOT_SUPPORTED;
}
@@ -3038,13 +3094,32 @@ apdu_shutdown_reader (int slot)
{
int sw;
+ if (DBG_READER)
+ log_debug ("enter: apdu_shutdown_reader: slot=%d\n", slot);
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return SW_HOST_NO_DRIVER;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_shutdown_reader => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
sw = apdu_disconnect (slot);
if (sw)
- return sw;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_shutdown_reader => 0x%x (apdu_disconnect)\n",
+ sw);
+ return sw;
+ }
if (reader_table[slot].shutdown_reader)
- return reader_table[slot].shutdown_reader (slot);
+ {
+ sw = reader_table[slot].shutdown_reader (slot);
+ if (DBG_READER)
+ log_debug ("leave: apdu_shutdown_reader => 0x%x (close_reader)\n", sw);
+ return sw;
+ }
+ if (DBG_READER)
+ log_debug ("leave: apdu_shutdown_reader => SW_HOST_NOT_SUPPORTED\n");
return SW_HOST_NOT_SUPPORTED;
}
@@ -3062,14 +3137,24 @@ apdu_enum_reader (int slot, int *used)
/* Connect a card. This is used to power up the card and make sure
- that an ATR is available. */
+ that an ATR is available. Depending on the reader backend it may
+ return an error for an inactive card or if no card is
+ available. */
int
apdu_connect (int slot)
{
int sw;
+ unsigned int status;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_connect: slot=%d\n", slot);
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return SW_HOST_NO_DRIVER;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_connect => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
/* Only if the access method provides a connect function we use it.
If not, we expect that the card has been implicitly connected by
@@ -3091,7 +3176,16 @@ apdu_connect (int slot)
scdaemon is fired up and apdu_get_status has not yet been called.
Without that we would force a reset of the card with the next
call to apdu_get_status. */
- apdu_get_status_internal (slot, 1, 1, NULL, NULL);
+ apdu_get_status_internal (slot, 1, 1, &status, NULL);
+ if (sw)
+ ;
+ else if (!(status & APDU_CARD_PRESENT))
+ sw = SW_HOST_NO_CARD;
+ else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
+ sw = SW_HOST_CARD_INACTIVE;
+
+ if (DBG_READER)
+ log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
return sw;
}
@@ -3102,8 +3196,15 @@ apdu_disconnect (int slot)
{
int sw;
+ if (DBG_READER)
+ log_debug ("enter: apdu_disconnect: slot=%d\n", slot);
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return SW_HOST_NO_DRIVER;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
if (reader_table[slot].disconnect_card)
{
@@ -3116,6 +3217,9 @@ apdu_disconnect (int slot)
}
else
sw = 0;
+
+ if (DBG_READER)
+ log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
return sw;
}
@@ -3151,11 +3255,22 @@ apdu_reset (int slot)
{
int sw;
+ if (DBG_READER)
+ log_debug ("enter: apdu_reset: slot=%d\n", slot);
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return SW_HOST_NO_DRIVER;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
if ((sw = lock_slot (slot)))
- return sw;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_reset => sw=0x%x (lock_slot)\n", sw);
+ return sw;
+ }
reader_table[slot].last_status = 0;
if (reader_table[slot].reset_reader)
@@ -3171,73 +3286,47 @@ apdu_reset (int slot)
}
unlock_slot (slot);
+ if (DBG_READER)
+ log_debug ("leave: apdu_reset => sw=0x%x\n", sw);
return sw;
}
-/* Activate a card if it has not yet been done. This is a kind of
- reset-if-required. It is useful to test for presence of a card
- before issuing a bunch of apdu commands. It does not wait on a
- locked card. */
-int
-apdu_activate (int slot)
-{
- int sw;
- unsigned int s;
-
- if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return SW_HOST_NO_DRIVER;
-
- if ((sw = trylock_slot (slot)))
- return sw;
-
- if (reader_table[slot].get_status_reader)
- sw = reader_table[slot].get_status_reader (slot, &s);
-
- if (!sw)
- {
- if (!(s & 2)) /* Card not present. */
- sw = SW_HOST_NO_CARD;
- else if ( ((s & 2) && !(s & 4))
- || !reader_table[slot].atrlen )
- {
- /* We don't have an ATR or a card is present though inactive:
- do a reset now. */
- if (reader_table[slot].reset_reader)
- {
- reader_table[slot].last_status = 0;
- sw = reader_table[slot].reset_reader (slot);
- if (!sw)
- {
- /* If we got to here we know that a card is present
- and usable. Thus remember this. */
- reader_table[slot].last_status = (APDU_CARD_USABLE
- | APDU_CARD_PRESENT
- | APDU_CARD_ACTIVE);
- }
- }
- }
- }
-
- unlock_slot (slot);
- return sw;
-}
-
-
+/* Return the ATR or NULL if none is available. On success the length
+ of the ATR is stored at ATRLEN. The caller must free the returned
+ value. */
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
{
unsigned char *buf;
+ if (DBG_READER)
+ log_debug ("enter: apdu_get_atr: slot=%d\n", slot);
+
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
- return NULL;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => NULL (bad slot)\n");
+ return NULL;
+ }
if (!reader_table[slot].atrlen)
- return NULL;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => NULL (no ATR)\n");
+ return NULL;
+ }
+
buf = xtrymalloc (reader_table[slot].atrlen);
if (!buf)
- return NULL;
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => NULL (out of core)\n");
+ return NULL;
+ }
memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
*atrlen = reader_table[slot].atrlen;
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen);
return buf;
}
@@ -3308,7 +3397,26 @@ int
apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed)
{
- return apdu_get_status_internal (slot, hang, 0, status, changed);
+ int sw;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_get_status: slot=%d hang=%d\n", slot, hang);
+ sw = apdu_get_status_internal (slot, hang, 0, status, changed);
+ if (DBG_READER)
+ {
+ if (status && changed)
+ log_debug ("leave: apdu_get_status => sw=0x%x status=%u changecnt=%u\n",
+ sw, *status, *changed);
+ else if (status)
+ log_debug ("leave: apdu_get_status => sw=0x%x status=%u\n",
+ sw, *status);
+ else if (changed)
+ log_debug ("leave: apdu_get_status => sw=0x%x changed=%u\n",
+ sw, *changed);
+ else
+ log_debug ("leave: apdu_get_status => sw=0x%x\n", sw);
+ }
+ return sw;
}
diff --git a/scd/apdu.h b/scd/apdu.h
index ac1eeeb3b..75025469e 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -41,6 +41,9 @@ enum {
SW_NOT_SUPPORTED = 0x6a81,
SW_FILE_NOT_FOUND = 0x6a82,
SW_RECORD_NOT_FOUND = 0x6a83,
+ SW_NOT_ENOUGH_MEMORY= 0x6a84, /* Not enough memory space in the file. */
+ SW_INCONSISTENT_LC = 0x6a85, /* Lc inconsistent with TLV structure. */
+ SW_INCORRECT_P0_P1 = 0x6a86,
SW_BAD_LC = 0x6a87, /* Lc does not match command or p1/p2. */
SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00,
@@ -108,7 +111,6 @@ int apdu_disconnect (int slot);
int apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg);
-int apdu_activate (int slot);
int apdu_reset (int slot);
int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed);
diff --git a/scd/app-common.h b/scd/app-common.h
index 6a1e2a763..e3d23c2b4 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -143,7 +143,8 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/
void app_dump_state (void);
void application_notify_card_reset (int slot);
-gpg_error_t check_application_conflict (ctrl_t ctrl, const char *name);
+gpg_error_t check_application_conflict (ctrl_t ctrl, int slot,
+ const char *name);
gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
app_t *r_app);
char *get_supported_applications (void);
diff --git a/scd/app.c b/scd/app.c
index 97260082e..79e3eac0f 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -198,18 +198,19 @@ application_notify_card_reset (int slot)
used to request a specific application and the connection has
already done a select_application. */
gpg_error_t
-check_application_conflict (ctrl_t ctrl, const char *name)
+check_application_conflict (ctrl_t ctrl, int slot, const char *name)
{
- int slot = ctrl->reader_slot;
app_t app;
+ (void)ctrl;
+
if (slot < 0 || slot >= DIM (lock_table))
return gpg_error (GPG_ERR_INV_VALUE);
app = lock_table[slot].initialized ? lock_table[slot].app : NULL;
if (app && app->apptype && name)
if ( ascii_strcasecmp (app->apptype, name))
- return gpg_error (GPG_ERR_CONFLICT);
+ return gpg_error (GPG_ERR_CONFLICT);
return 0;
}
@@ -226,11 +227,14 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
app_t app = NULL;
unsigned char *result = NULL;
size_t resultlen;
+ int want_undefined;
(void)ctrl;
*r_app = NULL;
+ want_undefined = (name && !strcmp (name, "undefined"));
+
err = lock_reader (slot, ctrl);
if (err)
return err;
@@ -310,45 +314,49 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
/* Fixme: We should now first check whether a card is at all
present. */
- /* Try to read the GDO file first to get a default serial number. */
- err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
- if (!err)
- err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
- if (!err)
- err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
- if (!err)
+ /* Try to read the GDO file first to get a default serial number.
+ We skip this if the undefined application has been requested. */
+ if (!want_undefined)
{
- size_t n;
- const unsigned char *p;
-
- p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
- if (p)
- resultlen -= (p-result);
- if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+ err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
+ if (!err)
+ err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
+ if (!err)
+ err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+ if (!err)
{
- /* The object it does not fit into the buffer. This is an
- invalid encoding (or the buffer is too short. However, I
- have some test cards with such an invalid encoding and
- therefore I use this ugly workaround to return something
- I can further experiment with. */
- log_info ("enabling BMI testcard workaround\n");
- n--;
+ size_t n;
+ const unsigned char *p;
+
+ p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
+ if (p)
+ resultlen -= (p-result);
+ if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+ {
+ /* The object it does not fit into the buffer. This is an
+ invalid encoding (or the buffer is too short. However, I
+ have some test cards with such an invalid encoding and
+ therefore I use this ugly workaround to return something
+ I can further experiment with. */
+ log_info ("enabling BMI testcard workaround\n");
+ n--;
+ }
+
+ if (p && n <= resultlen)
+ {
+ /* The GDO file is pretty short, thus we simply reuse it for
+ storing the serial number. */
+ memmove (result, p, n);
+ app->serialno = result;
+ app->serialnolen = n;
+ err = app_munge_serialno (app);
+ if (err)
+ goto leave;
+ }
+ else
+ xfree (result);
+ result = NULL;
}
-
- if (p && n <= resultlen)
- {
- /* The GDO file is pretty short, thus we simply reuse it for
- storing the serial number. */
- memmove (result, p, n);
- app->serialno = result;
- app->serialnolen = n;
- err = app_munge_serialno (app);
- if (err)
- goto leave;
- }
- else
- xfree (result);
- result = NULL;
}
/* For certain error codes, there is no need to try more. */
@@ -357,7 +365,15 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
goto leave;
/* Figure out the application to use. */
- err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (want_undefined)
+ {
+ /* We switch to the "undefined" application only if explicitly
+ requested. */
+ app->apptype = "UNDEFINED";
+ err = 0;
+ }
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
if (err && is_app_allowed ("openpgp")
&& (!name || !strcmp (name, "openpgp")))
@@ -366,11 +382,11 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
err = app_select_nks (app);
if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15")))
err = app_select_p15 (app);
- if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
- err = app_select_dinsig (app);
if (err && is_app_allowed ("geldkarte")
&& (!name || !strcmp (name, "geldkarte")))
err = app_select_geldkarte (app);
+ if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
+ err = app_select_dinsig (app);
if (err && name)
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
@@ -404,8 +420,10 @@ get_supported_applications (void)
"openpgp",
"nks",
"p15",
- "dinsig",
"geldkarte",
+ "dinsig",
+ /* Note: "undefined" is not listed here because it needs special
+ treatment by the client. */
NULL
};
int idx;
diff --git a/scd/atr.c b/scd/atr.c
index 16f26fb7d..b8668a41d 100644
--- a/scd/atr.c
+++ b/scd/atr.c
@@ -1,5 +1,5 @@
/* atr.c - ISO 7816 ATR fucntions
- * Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -24,10 +24,9 @@
#include <string.h>
#include <assert.h>
-#include "scdaemon.h"
-#include "apdu.h"
+#include "../common/estream.h"
+#include "../common/logging.h"
#include "atr.h"
-#include "dynload.h"
static int const fi_table[16] = { 0, 372, 558, 744, 1116,1488, 1860, -1,
-1, 512, 768, 1024, 1536, 2048, -1, -1 };
@@ -35,37 +34,42 @@ static int const di_table[16] = { -1, 1, 2, 4, 8, 16, -1, -1,
0, -1, -2, -4, -8, -16, -32, -64};
-/* Dump the ATR of the card at SLOT in a human readable format to
- stream FP. */
-int
-atr_dump (int slot, FILE *fp)
+/* Dump the ATR in (BUFFER,BUFLEN) to a human readable format and
+ return that as a malloced buffer. The caller must release this
+ buffer using es_free! On error this function returns NULL and sets
+ ERRNO. */
+char *
+atr_dump (const void *buffer, size_t buflen)
{
- unsigned char *atrbuffer, *atr;
- size_t atrlen;
+ const unsigned char *atr = buffer;
+ size_t atrlen = buflen;
+ estream_t fp;
int have_ta, have_tb, have_tc, have_td;
int n_historical;
int idx, val;
unsigned char chksum;
+ char *result;
- atr = atrbuffer = apdu_get_atr (slot, &atrlen);
- if (!atr)
- return gpg_error (GPG_ERR_GENERAL);
+ fp = es_fopenmem (0, "rwb");
+ if (!fp)
+ return NULL;
- fprintf (fp, "Info on ATR of length %u at slot %d\n",
- (unsigned int)atrlen, slot);
if (!atrlen)
{
- fprintf (fp, "error: empty ATR\n");
+ es_fprintf (fp, "error: empty ATR\n");
goto bailout;
}
+ for (idx=0; idx < atrlen ; idx++)
+ es_fprintf (fp, "%s%02X", idx?" ":"", atr[idx]);
+ es_putc ('\n', fp);
if (*atr == 0x3b)
- fputs ("direct convention\n", fp);
+ es_fputs ("Direct convention\n", fp);
else if (*atr == 0x3f)
- fputs ("inverse convention\n", fp);
+ es_fputs ("Inverse convention\n", fp);
else
- fprintf (fp,"error: invalid TS character 0x%02x\n", *atr);
+ es_fprintf (fp,"error: invalid TS character 0x%02x\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
@@ -79,34 +83,34 @@ atr_dump (int slot, FILE *fp)
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
n_historical = (*atr & 0x0f);
- fprintf (fp, "%d historical characters indicated\n", n_historical);
+ es_fprintf (fp, "%d historical characters indicated\n", n_historical);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
- fputs ("error: ATR shorter than indicated by format character\n", fp);
+ es_fputs ("error: ATR shorter than indicated by format character\n", fp);
if (!--atrlen)
goto bailout;
atr++;
if (have_ta)
{
- fputs ("TA1: F=", fp);
+ es_fputs ("TA1: F=", fp);
val = fi_table[(*atr >> 4) & 0x0f];
if (!val)
- fputs ("internal clock", fp);
+ es_fputs ("internal clock", fp);
else if (val == -1)
- fputs ("RFU", fp);
+ es_fputs ("RFU", fp);
else
- fprintf (fp, "%d", val);
- fputs (" D=", fp);
+ es_fprintf (fp, "%d", val);
+ es_fputs (" D=", fp);
val = di_table[*atr & 0x0f];
if (!val)
- fputs ("[impossible value]\n", fp);
+ es_fputs ("[impossible value]\n", fp);
else if (val == -1)
- fputs ("RFU\n", fp);
+ es_fputs ("RFU\n", fp);
else if (val < 0 )
- fprintf (fp, "1/%d\n", val);
+ es_fprintf (fp, "1/%d\n", val);
else
- fprintf (fp, "%d\n", val);
+ es_fprintf (fp, "%d\n", val);
if (!--atrlen)
goto bailout;
@@ -115,8 +119,9 @@ atr_dump (int slot, FILE *fp)
if (have_tb)
{
- fprintf (fp, "TB1: II=%d PI1=%d%s\n", (*atr >> 5) & 3, *atr & 0x1f,
- (*atr & 0x80)? " [high bit not cleared]":"");
+ es_fprintf (fp, "TB1: II=%d PI1=%d%s\n",
+ ((*atr >> 5) & 3), (*atr & 0x1f),
+ (*atr & 0x80)? " [high bit not cleared]":"");
if (!--atrlen)
goto bailout;
atr++;
@@ -125,9 +130,9 @@ atr_dump (int slot, FILE *fp)
if (have_tc)
{
if (*atr == 255)
- fputs ("TC1: guard time shortened to 1 etu\n", fp);
+ es_fputs ("TC1: guard time shortened to 1 etu\n", fp);
else
- fprintf (fp, "TC1: (extra guard time) N=%d\n", *atr);
+ es_fprintf (fp, "TC1: (extra guard time) N=%d\n", *atr);
if (!--atrlen)
goto bailout;
@@ -140,10 +145,11 @@ atr_dump (int slot, FILE *fp)
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
- fprintf (fp, "TD1: protocol T%d supported\n", *atr & 0x0f);
+ es_fprintf (fp, "TD1: protocol T%d supported\n", (*atr & 0x0f));
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
- fputs ("error: ATR shorter than indicated by format character\n", fp);
+ es_fputs ("error: ATR shorter than indicated by format character\n",
+ fp);
if (!--atrlen)
goto bailout;
@@ -154,12 +160,12 @@ atr_dump (int slot, FILE *fp)
if (have_ta)
{
- fprintf (fp, "TA2: (PTS) %stoggle, %splicit, T=%02X\n",
- (*atr & 0x80)? "no-":"",
- (*atr & 0x10)? "im": "ex",
- (*atr & 0x0f));
+ es_fprintf (fp, "TA2: (PTS) %stoggle, %splicit, T=%02X\n",
+ (*atr & 0x80)? "no-":"",
+ (*atr & 0x10)? "im": "ex",
+ (*atr & 0x0f));
if ((*atr & 0x60))
- fprintf (fp, "note: reserved bits are set (TA2=0x%02X)\n", *atr);
+ es_fprintf (fp, "note: reserved bits are set (TA2=0x%02X)\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
@@ -167,7 +173,7 @@ atr_dump (int slot, FILE *fp)
if (have_tb)
{
- fprintf (fp, "TB2: PI2=%d\n", *atr);
+ es_fprintf (fp, "TB2: PI2=%d\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
@@ -175,7 +181,7 @@ atr_dump (int slot, FILE *fp)
if (have_tc)
{
- fprintf (fp, "TC2: PWI=%d\n", *atr);
+ es_fprintf (fp, "TC2: PWI=%d\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
@@ -187,10 +193,11 @@ atr_dump (int slot, FILE *fp)
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
- fprintf (fp, "TD2: protocol T%d supported\n", *atr & 0x0f);
+ es_fprintf (fp, "TD2: protocol T%d supported\n", *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
- fputs ("error: ATR shorter than indicated by format character\n", fp);
+ es_fputs ("error: ATR shorter than indicated by format character\n",
+ fp);
if (!--atrlen)
goto bailout;
@@ -203,7 +210,7 @@ atr_dump (int slot, FILE *fp)
{
if (have_ta)
{
- fprintf (fp, "TA%d: IFSC=%d\n", idx, *atr);
+ es_fprintf (fp, "TA%d: IFSC=%d\n", idx, *atr);
if (!--atrlen)
goto bailout;
atr++;
@@ -211,7 +218,7 @@ atr_dump (int slot, FILE *fp)
if (have_tb)
{
- fprintf (fp, "TB%d: BWI=%d CWI=%d\n",
+ es_fprintf (fp, "TB%d: BWI=%d CWI=%d\n",
idx, (*atr >> 4) & 0x0f, *atr & 0x0f);
if (!--atrlen)
goto bailout;
@@ -220,7 +227,7 @@ atr_dump (int slot, FILE *fp)
if (have_tc)
{
- fprintf (fp, "TC%d: 0x%02X\n", idx, *atr);
+ es_fprintf (fp, "TC%d: 0x%02X\n", idx, *atr);
if (!--atrlen)
goto bailout;
atr++;
@@ -232,11 +239,12 @@ atr_dump (int slot, FILE *fp)
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
- fprintf (fp, "TD%d: protocol T%d supported\n", idx, *atr & 0x0f);
+ es_fprintf (fp, "TD%d: protocol T%d supported\n", idx, *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
- fputs ("error: ATR shorter than indicated by format character\n",
- fp);
+ es_fputs ("error: "
+ "ATR shorter than indicated by format character\n",
+ fp);
if (!--atrlen)
goto bailout;
@@ -247,150 +255,36 @@ atr_dump (int slot, FILE *fp)
}
if (n_historical + 1 > atrlen)
- fputs ("error: ATR shorter than required for historical bytes "
- "and checksum\n", fp);
+ es_fputs ("error: ATR shorter than required for historical bytes "
+ "and checksum\n", fp);
if (n_historical)
{
- fputs ("Historical:", fp);
+ es_fputs ("HCH:", fp);
for (; n_historical && atrlen ; n_historical--, atrlen--, atr++)
- fprintf (fp, " %02X", *atr);
- putchar ('\n');
+ es_fprintf (fp, " %02X", *atr);
+ es_putc ('\n', fp);
}
if (!atrlen)
- fputs ("error: checksum missing\n", fp);
+ es_fputs ("error: checksum missing\n", fp);
else if (*atr == chksum)
- fprintf (fp, "TCK: %02X (good)\n", *atr);
+ es_fprintf (fp, "TCK: %02X (good)\n", *atr);
else
- fprintf (fp, "TCK: %02X (bad; calculated %02X)\n", *atr, chksum);
+ es_fprintf (fp, "TCK: %02X (bad; computed %02X)\n", *atr, chksum);
atrlen--;
if (atrlen)
- fprintf (fp, "error: %u bytes garbage at end of ATR\n",
- (unsigned int)atrlen );
+ es_fprintf (fp, "error: %u bytes garbage at end of ATR\n",
+ (unsigned int)atrlen );
bailout:
- xfree (atrbuffer);
-
- return 0;
-}
-
-
-/* Note: This code has not yet been tested! It shall return -1 on
- error or the number of historical bytes and store them at
- HISTORICAL. */
-int
-atr_get_historical (int slot, unsigned char historical[])
-{
- int result = -1;
- unsigned char *atrbuffer = NULL;
- unsigned char *atr;
- size_t atrlen;
- int have_ta, have_tb, have_tc, have_td;
- int n_historical;
- int idx;
- unsigned char chksum;
-
- atr = atrbuffer = apdu_get_atr (slot, &atrlen);
- if (!atr || atrlen < 2)
- goto leave;
- atrlen--;
- atr++;
-
- chksum = *atr;
- for (idx=1; idx < atrlen-1; idx++)
- chksum ^= atr[idx];
-
- have_ta = !!(*atr & 0x10);
- have_tb = !!(*atr & 0x20);
- have_tc = !!(*atr & 0x40);
- have_td = !!(*atr & 0x80);
- n_historical = (*atr & 0x0f);
-
- if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
- goto leave; /* ATR shorter than indicated by format character. */
- atrlen--;
- atr++;
-
- if (have_ta + have_tb + have_tc >= atrlen)
- goto leave;
- atrlen -= have_ta + have_tb + have_tc;
- atr += have_ta + have_tb + have_tc;
-
- if (have_td)
- {
- have_ta = !!(*atr & 0x10);
- have_tb = !!(*atr & 0x20);
- have_tc = !!(*atr & 0x40);
- have_td = !!(*atr & 0x80);
- if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
- goto leave; /* ATR shorter than indicated by format character. */
- atrlen--;
- atr++;
- }
- else
- have_ta = have_tb = have_tc = have_td = 0;
-
- if (have_ta + have_tb + have_tc >= atrlen)
- goto leave;
- atrlen -= have_ta + have_tb + have_tc;
- atr += have_ta + have_tb + have_tc;
-
- if (have_td)
+ es_putc ('\0', fp); /* We want a string. */
+ if (es_fclose_snatch (fp, (void**)&result, NULL))
{
- have_ta = !!(*atr & 0x10);
- have_tb = !!(*atr & 0x20);
- have_tc = !!(*atr & 0x40);
- have_td = !!(*atr & 0x80);
- if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
- goto leave; /* ATR shorter than indicated by format character. */
- atrlen--;
- atr++;
+ log_error ("oops: es_fclose_snatch failed: %s\n", strerror (errno));
+ return NULL;
}
- else
- have_ta = have_tb = have_tc = have_td = 0;
-
- for (idx = 3; have_ta || have_tb || have_tc || have_td; idx++)
- {
- if (have_ta + have_tb + have_tc >= atrlen)
- goto leave;
- atrlen -= have_ta + have_tb + have_tc;
- atr += have_ta + have_tb + have_tc;
-
- if (have_td)
- {
- have_ta = !!(*atr & 0x10);
- have_tb = !!(*atr & 0x20);
- have_tc = !!(*atr & 0x40);
- have_td = !!(*atr & 0x80);
- if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
- goto leave; /* ATR shorter than indicated by format character. */
- atrlen--;
- atr++;
- }
- else
- have_ta = have_tb = have_tc = have_td = 0;
- }
-
- if (n_historical >= atrlen)
- goto leave; /* ATR shorter than required for historical bytes. */
-
- if (n_historical)
- {
- for (idx=0; n_historical && atrlen; n_historical--, atrlen--, atr++)
- historical[idx] = *atr;
- }
-
- if (!atrlen || *atr != chksum)
- goto leave;
-
- /* Don't care about garbage at the end of the ATR. */
-
- result = n_historical;
-
- leave:
- xfree (atrbuffer);
return result;
}
diff --git a/scd/atr.h b/scd/atr.h
index 5f07522d1..b06a83a60 100644
--- a/scd/atr.h
+++ b/scd/atr.h
@@ -20,7 +20,7 @@
#ifndef ATR_H
#define ATR_H
-int atr_dump (int slot, FILE *fp);
+char *atr_dump (const void *buffer, size_t buflen);
diff --git a/scd/command.c b/scd/command.c
index fe409d560..5e7feb5be 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -1,6 +1,6 @@
/* command.c - SCdaemon command handler
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
- * 2007, 2008, 2009 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -35,11 +35,13 @@
#include <ksba.h>
#include "app-common.h"
#include "apdu.h" /* Required for apdu_*_reader (). */
+#include "atr.h"
#include "exechelp.h"
#ifdef HAVE_LIBUSB
#include "ccid-driver.h"
#endif
+
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
#define MAXLEN_PIN 100
@@ -60,33 +62,39 @@
int _r = (r); \
if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \
|| gpg_err_code (_r) == GPG_ERR_CARD_REMOVED \
+ || gpg_err_code (_r) == GPG_ERR_CARD_RESET \
|| gpg_err_code (_r) == GPG_ERR_ENODEV ) \
- update_card_removed ((c)->reader_slot, 1); \
+ update_card_removed ((c)->server_local->vreader_idx, 1); \
} while (0)
-#define IS_LOCKED(c) \
- (locked_session && locked_session != (c)->server_local \
- && (c)->reader_slot != -1 && locked_session->ctrl_backlink \
- && (c)->reader_slot == locked_session->ctrl_backlink->reader_slot)
+#define IS_LOCKED(c) \
+ (locked_session \
+ && locked_session != (c)->server_local \
+ && (c)->server_local->vreader_idx != -1 \
+ && locked_session->ctrl_backlink \
+ && ((c)->server_local->vreader_idx \
+ == locked_session->ctrl_backlink->server_local->vreader_idx))
/* Flag indicating that the reader has been disabled. */
static int reader_disabled;
-/* This structure is used to keep track of open readers (slots). */
-struct slot_status_s
+/* This structure is used to keep track of user readers. To
+ eventually accommodate this structure for RFID cards, where more
+ than one card is used per reader, we name it virtual reader. */
+struct vreader_s
{
int valid; /* True if the other objects are valid. */
- int slot; /* Slot number of the reader or -1 if not open. */
+ int slot; /* APDU slot number of the reader or -1 if not open. */
int reset_failed; /* A reset failed. */
int any; /* Flag indicating whether any status check has been
done. This is set once to indicate that the status
tracking for the slot has been initialized. */
- unsigned int status; /* Last status of the slot. */
- unsigned int changed; /* Last change counter of the slot. */
+ unsigned int status; /* Last status of the reader. */
+ unsigned int changed; /* Last change counter of the reader. */
};
@@ -113,6 +121,9 @@ struct server_local_s
int event_signal; /* Or 0 if not used. */
#endif
+ /* Index into the vreader table (command.c) or -1 if not open. */
+ int vreader_idx;
+
/* True if the card has been removed and a reset is required to
continue operation. */
int card_removed;
@@ -131,10 +142,8 @@ struct server_local_s
};
-/* The table with information on all used slots. FIXME: This is a
- different slot number than the one used by the APDU layer, and
- should be renamed. */
-static struct slot_status_s slot_table[10];
+/* The table with information on all used virtual readers. */
+static struct vreader_s vreader_table[10];
/* To keep track of all running sessions, we link all active server
@@ -175,26 +184,41 @@ initialize_module_command (void)
}
-/* Update the CARD_REMOVED element of all sessions using the reader
- given by SLOT to VALUE. */
+/* Helper to return the slot number for a given virtual reader index
+ VRDR. In case on an error -1 is returned. */
+static int
+vreader_slot (int vrdr)
+{
+ if (vrdr == -1 || !(vrdr >= 0 && vrdr < DIM(vreader_table)))
+ return -1;
+ if (!vreader_table [vrdr].valid)
+ return -1;
+ return vreader_table[vrdr].slot;
+}
+
+
+/* Update the CARD_REMOVED element of all sessions using the virtual
+ reader given by VRDR to VALUE. */
static void
-update_card_removed (int slot, int value)
+update_card_removed (int vrdr, int value)
{
struct server_local_s *sl;
+ if (vrdr == -1)
+ return;
+
for (sl=session_list; sl; sl = sl->next_session)
if (sl->ctrl_backlink
- && sl->ctrl_backlink->reader_slot == slot)
+ && sl->ctrl_backlink->server_local->vreader_idx == vrdr)
{
sl->card_removed = value;
}
/* Let the card application layer know about the removal. */
if (value)
- application_notify_card_reset (slot);
+ application_notify_card_reset (vreader_slot (vrdr));
}
-
/* Check whether the option NAME appears in LINE. Returns 1 or 0. */
static int
has_option (const char *line, const char *name)
@@ -280,10 +304,10 @@ hex_to_buffer (const char *string, size_t *r_length)
static void
do_reset (ctrl_t ctrl, int send_reset)
{
- int slot = ctrl->reader_slot;
- int err;
+ int vrdr = ctrl->server_local->vreader_idx;
+ int slot;
- if (!(slot == -1 || (slot >= 0 && slot < DIM(slot_table))))
+ if (!(vrdr == -1 || (vrdr >= 0 && vrdr < DIM(vreader_table))))
BUG ();
/* If there is an active application, release it. Tell all other
@@ -299,7 +323,7 @@ do_reset (ctrl_t ctrl, int send_reset)
for (sl=session_list; sl; sl = sl->next_session)
if (sl->ctrl_backlink
- && sl->ctrl_backlink->reader_slot == slot)
+ && sl->ctrl_backlink->server_local->vreader_idx == vrdr)
{
sl->app_ctx_marked_for_release = 1;
}
@@ -308,13 +332,22 @@ do_reset (ctrl_t ctrl, int send_reset)
/* If we want a real reset for the card, send the reset APDU and
tell the application layer about it. */
+ slot = vreader_slot (vrdr);
if (slot != -1 && send_reset && !IS_LOCKED (ctrl) )
{
- if (apdu_reset (slot))
+ application_notify_card_reset (slot);
+ switch (apdu_reset (slot))
{
- slot_table[slot].valid = 0;
+ case 0:
+ break;
+ case SW_HOST_NO_CARD:
+ case SW_HOST_CARD_INACTIVE:
+ break;
+ default:
+ apdu_close_reader (slot);
+ vreader_table[vrdr].slot = slot = -1;
+ break;
}
- application_notify_card_reset (slot);
}
/* If we hold a lock, unlock now. */
@@ -327,25 +360,24 @@ do_reset (ctrl_t ctrl, int send_reset)
/* Reset the card removed flag for the current reader. We need to
take the lock here so that the ticker thread won't concurrently
try to update the file. Calling update_reader_status_file is
- required to get hold of the new status of the card in the slot
+ required to get hold of the new status of the card in the vreader
table. */
err = npth_mutex_lock (&status_file_update_lock);
if (err)
{
- log_error ("failed to acquire status_fle_update lock: %s\n",
- strerror (err));
- ctrl->reader_slot = -1;
+ log_error ("failed to acquire status_file_update lock\n");
+ ctrl->server_local->vreader_idx = -1;
return;
}
update_reader_status_file (0); /* Update slot status table. */
- update_card_removed (slot, 0); /* Clear card_removed flag. */
+ update_card_removed (vrdr, 0); /* Clear card_removed flag. */
err = npth_mutex_unlock (&status_file_update_lock);
if (err)
log_error ("failed to release status_file_update lock: %s\n",
strerror (err));
/* Do this last, so that the update_card_removed above does its job. */
- ctrl->reader_slot = -1;
+ ctrl->server_local->vreader_idx = -1;
}
@@ -385,35 +417,38 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
}
-/* Return the slot of the current reader or open the reader if no
- other sessions are using a reader. Note, that we currently support
+/* Return the index of the current reader or open the reader if no
+ other sessions are using that reader. If it is not possible to
+ open the reader -1 is returned. Note, that we currently support
only one reader but most of the code (except for this function)
should be able to cope with several readers. */
static int
-get_reader_slot (void)
+get_current_reader (void)
{
- struct slot_status_s *ss;
+ struct vreader_s *vr;
- ss = &slot_table[0]; /* One reader for now. */
+ /* We only support one reader for now. */
+ vr = &vreader_table[0];
- /* Initialize the item if needed. */
- if (!ss->valid)
+ /* Initialize the vreader item if not yet done. */
+ if (!vr->valid)
{
- ss->slot = -1;
- ss->valid = 1;
+ vr->slot = -1;
+ vr->valid = 1;
}
/* Try to open the reader. */
- if (ss->slot == -1)
+ if (vr->slot == -1)
{
int no_service_flag;
- ss->slot = apdu_open_reader (opt.reader_port, &no_service_flag);
+
+ vr->slot = apdu_open_reader (opt.reader_port, &no_service_flag);
/* If we still don't have a slot, we have no readers.
Invalidate for now until a reader is attached. */
- if(ss->slot == -1)
+ if (vr->slot == -1)
{
- ss->valid = 0;
+ vr->valid = 0;
}
if (no_service_flag)
@@ -423,18 +458,17 @@ get_reader_slot (void)
}
}
- /* Return the slot_table index. */
- return 0;
+ /* Return the vreader index or -1. */
+ return vr->valid ? 0 : -1;
}
-/* If the card has not yet been opened, do it. Note that this
- function returns an Assuan error, so don't map the error a second
- time. */
+
+/* If the card has not yet been opened, do it. */
static gpg_error_t
open_card (ctrl_t ctrl, const char *apptype)
{
gpg_error_t err;
- int slot;
+ int vrdr;
if (reader_disabled)
return gpg_error (GPG_ERR_NOT_OPERATIONAL);
@@ -462,21 +496,25 @@ open_card (ctrl_t ctrl, const char *apptype)
need to check that the client didn't requested a specific
application different from the one in use before we continue. */
if (ctrl->app_ctx)
- return check_application_conflict (ctrl, apptype);
+ {
+ return check_application_conflict
+ (ctrl, vreader_slot (ctrl->server_local->vreader_idx), apptype);
+ }
- /* Setup the slot and select the application. */
- if (ctrl->reader_slot != -1)
- slot = ctrl->reader_slot;
+ /* Setup the vreader and select the application. */
+ if (ctrl->server_local->vreader_idx != -1)
+ vrdr = ctrl->server_local->vreader_idx;
else
- slot = get_reader_slot ();
- ctrl->reader_slot = slot;
- if (slot == -1)
+ vrdr = get_current_reader ();
+ ctrl->server_local->vreader_idx = vrdr;
+ if (vrdr == -1)
err = gpg_error (reader_disabled? GPG_ERR_NOT_OPERATIONAL: GPG_ERR_CARD);
else
{
/* Fixme: We should move the apdu_connect call to
select_application. */
int sw;
+ int slot = vreader_slot (vrdr);
ctrl->server_local->disconnect_allowed = 0;
sw = apdu_connect (slot);
@@ -484,6 +522,8 @@ open_card (ctrl_t ctrl, const char *apptype)
{
if (sw == SW_HOST_NO_CARD)
err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+ else if (sw == SW_HOST_CARD_INACTIVE)
+ err = gpg_error (GPG_ERR_CARD_RESET);
else
err = gpg_error (GPG_ERR_CARD);
}
@@ -523,8 +563,10 @@ cmd_serialno (assuan_context_t ctx, char *line)
char *serial_and_stamp;
char *serial;
time_t stamp;
+ int retries = 0;
/* Clear the remove flag so that the open_card is able to reread it. */
+ retry:
if (!reader_disabled && ctrl->server_local->card_removed)
{
if ( IS_LOCKED (ctrl) )
@@ -533,7 +575,12 @@ cmd_serialno (assuan_context_t ctx, char *line)
}
if ((rc = open_card (ctrl, *line? line:NULL)))
- return rc;
+ {
+ /* In case of an inactive card, retry once. */
+ if (gpg_err_code (rc) == GPG_ERR_CARD_RESET && retries++ < 1)
+ goto retry;
+ return rc;
+ }
rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
if (rc)
@@ -1613,8 +1660,8 @@ static const char hlp_getinfo[] =
"\n"
"socket_name - Return the name of the socket.\n"
"\n"
- "status - Return the status of the current slot (in the future, may\n"
- "also return the status of all slots). The status is a list of\n"
+ "status - Return the status of the current reader (in the future, may\n"
+ "also return the status of all readers). The status is a list of\n"
"one-character flags. The following flags are currently defined:\n"
" 'u' Usable card present. This is the normal state during operation.\n"
" 'r' Card removed. A reset is necessary.\n"
@@ -1658,22 +1705,18 @@ cmd_getinfo (assuan_context_t ctx, char *line)
else if (!strcmp (line, "status"))
{
ctrl_t ctrl = assuan_get_pointer (ctx);
- int slot = ctrl->reader_slot;
+ int vrdr = ctrl->server_local->vreader_idx;
char flag = 'r';
- if (!ctrl->server_local->card_removed && slot != -1)
+ if (!ctrl->server_local->card_removed && vrdr != -1)
{
- struct slot_status_s *ss;
+ struct vreader_s *vr;
- if (!(slot >= 0 && slot < DIM(slot_table)))
+ if (!(vrdr >= 0 && vrdr < DIM(vreader_table)))
BUG ();
- ss = &slot_table[slot];
-
- if (!ss->valid)
- BUG ();
-
- if (ss->any && (ss->status & 1))
+ vr = &vreader_table[vrdr];
+ if (vr->valid && vr->any && (vr->status & 1))
flag = 'u';
}
rc = assuan_send_data (ctx, &flag, 1);
@@ -1759,7 +1802,7 @@ cmd_disconnect (assuan_context_t ctx, char *line)
static const char hlp_apdu[] =
- "APDU [--atr] [--more] [--exlen[=N]] [hexstring]\n"
+ "APDU [--[dump-]atr] [--more] [--exlen[=N]] [hexstring]\n"
"\n"
"Send an APDU to the current reader. This command bypasses the high\n"
"level functions and sends the data directly to the card. HEXSTRING\n"
@@ -1788,8 +1831,12 @@ cmd_apdu (assuan_context_t ctx, char *line)
int handle_more;
const char *s;
size_t exlen;
+ int slot;
- with_atr = has_option (line, "--atr");
+ if (has_option (line, "--dump-atr"))
+ with_atr = 2;
+ else
+ with_atr = has_option (line, "--atr");
handle_more = has_option (line, "--more");
if ((s=has_option_name (line, "--exlen")))
@@ -1810,21 +1857,46 @@ cmd_apdu (assuan_context_t ctx, char *line)
if ((rc = open_card (ctrl, NULL)))
return rc;
+ slot = vreader_slot (ctrl->server_local->vreader_idx);
+
if (with_atr)
{
unsigned char *atr;
size_t atrlen;
char hexbuf[400];
- atr = apdu_get_atr (ctrl->reader_slot, &atrlen);
+ atr = apdu_get_atr (slot, &atrlen);
if (!atr || atrlen > sizeof hexbuf - 2 )
{
rc = gpg_error (GPG_ERR_INV_CARD);
goto leave;
}
- bin2hex (atr, atrlen, hexbuf);
+ if (with_atr == 2)
+ {
+ char *string, *p, *pend;
+
+ string = atr_dump (atr, atrlen);
+ if (string)
+ {
+ for (rc=0, p=string; !rc && (pend = strchr (p, '\n')); p = pend+1)
+ {
+ rc = assuan_send_data (ctx, p, pend - p + 1);
+ if (!rc)
+ rc = assuan_send_data (ctx, NULL, 0);
+ }
+ if (!rc && *p)
+ rc = assuan_send_data (ctx, p, strlen (p));
+ es_free (string);
+ if (rc)
+ goto leave;
+ }
+ }
+ else
+ {
+ bin2hex (atr, atrlen, hexbuf);
+ send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0);
+ }
xfree (atr);
- send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0);
}
apdu = hex_to_buffer (line, &apdulen);
@@ -1838,7 +1910,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
unsigned char *result = NULL;
size_t resultlen;
- rc = apdu_send_direct (ctrl->reader_slot, exlen,
+ rc = apdu_send_direct (slot, exlen,
apdu, apdulen, handle_more,
&result, &resultlen);
if (rc)
@@ -1869,15 +1941,8 @@ cmd_killscd (assuan_context_t ctx, char *line)
(void)line;
ctrl->server_local->stopme = 1;
-#ifdef ASSUAN_FORCE_CLOSE
assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
return 0;
-#else
- /* Actually returning an EOF does not anymore work with modern
- Libassuan versions. However we keep that non working code until
- we make a Libassuan with the force close flag a requirement. */
- return gpg_error (GPG_ERR_EOF);
-#endif
}
@@ -1988,12 +2053,13 @@ scd_command_handler (ctrl_t ctrl, int fd)
session_list = ctrl->server_local;
ctrl->server_local->ctrl_backlink = ctrl;
ctrl->server_local->assuan_ctx = ctx;
+ ctrl->server_local->vreader_idx = -1;
/* We open the reader right at startup so that the ticker is able to
update the status file. */
- if (ctrl->reader_slot == -1)
+ if (ctrl->server_local->vreader_idx == -1)
{
- ctrl->reader_slot = get_reader_slot ();
+ ctrl->server_local->vreader_idx = get_current_reader ();
}
/* Command processing loop. */
@@ -2195,32 +2261,33 @@ update_reader_status_file (int set_card_removed_flag)
int idx;
unsigned int status, changed;
- /* Make sure that the reader has been opened. Like get_reader_slot,
+ /* Make sure that a reader has been opened. Like get_current_reader,
this part of the code assumes that there is only one reader. */
- if (!slot_table[0].valid)
- (void)get_reader_slot ();
+ if (!vreader_table[0].valid)
+ (void)get_current_reader ();
/* Note, that we only try to get the status, because it does not
make sense to wait here for a operation to complete. If we are
busy working with a card, delays in the status file update should
be acceptable. */
- for (idx=0; idx < DIM(slot_table); idx++)
+ for (idx=0; idx < DIM(vreader_table); idx++)
{
- struct slot_status_s *ss = slot_table + idx;
+ struct vreader_s *vr = vreader_table + idx;
struct server_local_s *sl;
int sw_apdu;
- if (!ss->valid || ss->slot == -1)
+ if (!vr->valid || vr->slot == -1)
continue; /* Not valid or reader not yet open. */
- sw_apdu = apdu_get_status (ss->slot, 0, &status, &changed);
+ sw_apdu = apdu_get_status (vr->slot, 0, &status, &changed);
if (sw_apdu == SW_HOST_NO_READER)
{
/* Most likely the _reader_ has been unplugged. */
- apdu_close_reader(ss->slot);
- ss->valid = 0;
+ application_notify_card_reset (vr->slot);
+ apdu_close_reader (vr->slot);
+ vr->slot = -1;
status = 0;
- changed = ss->changed;
+ changed = vr->changed;
}
else if (sw_apdu)
{
@@ -2228,21 +2295,21 @@ update_reader_status_file (int set_card_removed_flag)
continue;
}
- if (!ss->any || ss->status != status || ss->changed != changed )
+ if (!vr->any || vr->status != status || vr->changed != changed )
{
char *fname;
char templ[50];
FILE *fp;
- log_info ("updating slot %d status: 0x%04X->0x%04X (%u->%u)\n",
- ss->slot, ss->status, status, ss->changed, changed);
- ss->status = status;
- ss->changed = changed;
+ log_info ("updating reader %d (%d) status: 0x%04X->0x%04X (%u->%u)\n",
+ idx, vr->slot, vr->status, status, vr->changed, changed);
+ vr->status = status;
+ vr->changed = changed;
- /* FIXME: Should this be IDX instead of ss->slot? This
+ /* FIXME: Should this be IDX instead of vr->slot? This
depends on how client sessions will associate the reader
status with their session. */
- snprintf (templ, sizeof templ, "reader_%d.status", ss->slot);
+ snprintf (templ, sizeof templ, "reader_%d.status", vr->slot);
fname = make_filename (opt.homedir, templ, NULL );
fp = fopen (fname, "w");
if (fp)
@@ -2270,8 +2337,8 @@ update_reader_status_file (int set_card_removed_flag)
envs[0] = envstr;
envs[1] = NULL;
- sprintf (numbuf1, "%d", ss->slot);
- sprintf (numbuf2, "0x%04X", ss->status);
+ sprintf (numbuf1, "%d", vr->slot);
+ sprintf (numbuf2, "0x%04X", vr->status);
sprintf (numbuf3, "0x%04X", status);
args[0] = "--reader-port";
args[1] = numbuf1;
@@ -2299,10 +2366,10 @@ update_reader_status_file (int set_card_removed_flag)
/* Set the card removed flag for all current sessions. We
will set this on any card change because a reset or
SERIALNO request must be done in any case. */
- if (ss->any && set_card_removed_flag)
+ if (vr->any && set_card_removed_flag)
update_card_removed (idx, 1);
- ss->any = 1;
+ vr->any = 1;
/* Send a signal to all clients who applied for it. */
send_client_notifications ();
@@ -2318,8 +2385,9 @@ update_reader_status_file (int set_card_removed_flag)
{
/* FIXME: Use a real timeout. */
/* At least one connection and all allow a disconnect. */
- log_info ("disconnecting card in slot %d\n", ss->slot);
- apdu_disconnect (ss->slot);
+ log_info ("disconnecting card in reader %d (%d)\n",
+ idx, vr->slot);
+ apdu_disconnect (vr->slot);
}
}
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 5b24a09f8..f674cfc76 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -74,6 +74,7 @@ enum cmd_and_opt_values
oDebugAllowCoreDump,
oDebugCCIDDriver,
oDebugLogTid,
+ oDebugAssuanLogCats,
oNoGreeting,
oNoOptions,
oHomedir,
@@ -123,6 +124,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"),
ARGPARSE_s_n (oDebugDisableTicker, "debug-disable-ticker", "@"),
ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"),
+ ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"),
ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a log to FILE")),
ARGPARSE_s_s (oReaderPort, "reader-port",
@@ -542,6 +544,9 @@ main (int argc, char **argv )
case oDebugLogTid:
log_set_pid_suffix_cb (tid_log_callback);
break;
+ case oDebugAssuanLogCats:
+ set_libassuan_log_cats (pargs.r.ret_ulong);
+ break;
case oOptions:
/* config files may not be nested (silently ignore them) */
@@ -826,7 +831,7 @@ main (int argc, char **argv )
if (csh_style)
{
*strchr (infostr, '=') = ' ';
- es_printf ( "setenv %s\n", infostr);
+ es_printf ( "setenv %s;\n", infostr);
}
else
{
@@ -909,7 +914,7 @@ scd_exit (int rc)
static void
scd_init_default_ctrl (ctrl_t ctrl)
{
- ctrl->reader_slot = -1;
+ (void)ctrl;
}
static void
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 0cf2f249d..74e8b7d44 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -72,8 +72,9 @@ struct
#define DBG_CACHE_VALUE 64 /* debug the caching */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
-#define DBG_ASSUAN_VALUE 1024
+#define DBG_ASSUAN_VALUE 1024
#define DBG_CARD_IO_VALUE 2048
+#define DBG_READER_VALUE 4096 /* Trace reader related functions. */
#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
@@ -82,6 +83,7 @@ struct
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE)
#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
+#define DBG_READER (opt.debug & DBG_READER_VALUE)
struct server_local_s;
struct app_ctx_s;
@@ -97,9 +99,6 @@ struct server_control_s
/* Local data of the server; used only in command.c. */
struct server_local_s *server_local;
- /* Slot of the open reader or -1 if not open. */
- int reader_slot;
-
/* The application context used with this connection or NULL if none
associated. Note that this is shared with the other connections:
All connections accessing the same reader are using the same