mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-03 22:56:33 +02:00
Fixed some card related problems.
This commit is contained in:
parent
6a09ba60cc
commit
7bd2e417d1
32 changed files with 3909 additions and 3722 deletions
|
@ -1,3 +1,17 @@
|
|||
2008-12-18 Werner Koch <wk@g10code.com>
|
||||
|
||||
* ccid-driver.c (abort_cmd): New.
|
||||
(bulk_in): Call abort_cmd after severe errors.
|
||||
|
||||
* apdu.c (reader_table_s): Add field ANY_STATUS.
|
||||
(new_reader_slot): Clear it.
|
||||
(apdu_get_status): Use ANY_STATUS to update the change counter.
|
||||
Remove the use of the flag bit from LAST_STATUS everywhere.
|
||||
* command.c (update_reader_status_file): Factor code out to ...
|
||||
(send_client_notifications): New. Track signals already sent.
|
||||
(update_reader_status_file): Shutdown the reader after a failed
|
||||
apdu_get_status.
|
||||
|
||||
2008-12-09 Werner Koch <wk@g10code.com>
|
||||
|
||||
* scdaemon.c (main): Call i18n_init before init_common_subsystems.
|
||||
|
|
28
scd/apdu.c
28
scd/apdu.c
|
@ -133,6 +133,7 @@ struct reader_table_s {
|
|||
} rapdu;
|
||||
#endif /*USE_G10CODE_RAPDU*/
|
||||
char *rdrname; /* Name of the connected reader or NULL if unknown. */
|
||||
int any_status; /* True if we have seen any status. */
|
||||
int last_status;
|
||||
int status;
|
||||
int is_t0; /* True if we know that we are running T=0. */
|
||||
|
@ -340,7 +341,8 @@ new_reader_slot (void)
|
|||
reader_table[reader].check_keypad = NULL;
|
||||
reader_table[reader].dump_status_reader = NULL;
|
||||
|
||||
reader_table[reader].used = 1;
|
||||
reader_table[reader].used = 1;
|
||||
reader_table[reader].any_status = 0;
|
||||
reader_table[reader].last_status = 0;
|
||||
reader_table[reader].is_t0 = 1;
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
|
@ -1289,8 +1291,7 @@ connect_pcsc_card (int slot)
|
|||
and usable. Remember this. */
|
||||
reader_table[slot].last_status = ( APDU_CARD_USABLE
|
||||
| APDU_CARD_PRESENT
|
||||
| APDU_CARD_ACTIVE
|
||||
| 0x8000);
|
||||
| APDU_CARD_ACTIVE);
|
||||
reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
|
||||
}
|
||||
}
|
||||
|
@ -1743,8 +1744,7 @@ open_pcsc_reader_wrapped (const char *portstr)
|
|||
and usable. Thus remember this. */
|
||||
slotp->last_status = ( APDU_CARD_USABLE
|
||||
| APDU_CARD_PRESENT
|
||||
| APDU_CARD_ACTIVE
|
||||
| 0x8000);
|
||||
| APDU_CARD_ACTIVE);
|
||||
}
|
||||
slotp->atrlen = len;
|
||||
|
||||
|
@ -1947,8 +1947,7 @@ open_ccid_reader (const char *portstr)
|
|||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (APDU_CARD_USABLE
|
||||
| APDU_CARD_PRESENT
|
||||
| APDU_CARD_ACTIVE
|
||||
| 0x8000);
|
||||
| APDU_CARD_ACTIVE);
|
||||
}
|
||||
|
||||
reader_table[slot].close_reader = close_ccid_reader;
|
||||
|
@ -2615,8 +2614,7 @@ apdu_reset (int slot)
|
|||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (APDU_CARD_USABLE
|
||||
| APDU_CARD_PRESENT
|
||||
| APDU_CARD_ACTIVE
|
||||
| 0x8000);
|
||||
| APDU_CARD_ACTIVE);
|
||||
}
|
||||
|
||||
unlock_slot (slot);
|
||||
|
@ -2662,8 +2660,7 @@ apdu_activate (int slot)
|
|||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (APDU_CARD_USABLE
|
||||
| APDU_CARD_PRESENT
|
||||
| APDU_CARD_ACTIVE
|
||||
| 0x8000);
|
||||
| APDU_CARD_ACTIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2733,17 +2730,16 @@ apdu_get_status (int slot, int hang,
|
|||
return sw;
|
||||
}
|
||||
|
||||
/* Keep track of changes. We use one extra bit to test whether we
|
||||
have checked the status at least once. */
|
||||
if ( s != (reader_table[slot].last_status & 0x07ff)
|
||||
|| !reader_table[slot].last_status )
|
||||
/* Keep track of changes. */
|
||||
if (s != reader_table[slot].last_status
|
||||
|| !reader_table[slot].any_status )
|
||||
{
|
||||
reader_table[slot].change_counter++;
|
||||
/* Make sure that the ATR is invalid so that a reset will be by
|
||||
activate. */
|
||||
reader_table[slot].atrlen = 0;
|
||||
}
|
||||
reader_table[slot].last_status = (s | 0x8000);
|
||||
reader_table[slot].any_status = 1;
|
||||
|
||||
if (status)
|
||||
*status = s;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
|
||||
* Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2003, 2004, 2005, 2006, 2007
|
||||
* 2008 Free Software Foundation, Inc.
|
||||
* Written by Werner Koch.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
|
@ -262,6 +263,7 @@ 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,
|
||||
size_t *nread, int expected_type, int seqno, int timeout,
|
||||
int no_debug);
|
||||
static int abort_cmd (ccid_driver_t handle);
|
||||
|
||||
/* Convert a little endian stored 4 byte value into an unsigned
|
||||
integer. */
|
||||
|
@ -1069,7 +1071,10 @@ scan_or_find_devices (int readerno, const char *readerid,
|
|||
/* Set the level of debugging to LEVEL and return the old level. -1
|
||||
just returns the old level. A level of 0 disables debugging, 1
|
||||
enables debugging, 2 enables additional tracing of the T=1
|
||||
protocol, other values are not yet defined. */
|
||||
protocol, other values are not yet defined.
|
||||
|
||||
Note that libusb may provide its own debugging feature which is
|
||||
enabled by setting the envvar USB_DEBUG. */
|
||||
int
|
||||
ccid_set_debug_level (int level)
|
||||
{
|
||||
|
@ -1395,7 +1400,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
|
|||
rc = usb_bulk_write (handle->idev,
|
||||
handle->ep_bulk_out,
|
||||
(char*)msg, msglen,
|
||||
1000 /* ms timeout */);
|
||||
5000 /* ms timeout */);
|
||||
if (rc == msglen)
|
||||
return 0;
|
||||
if (rc == -1)
|
||||
|
@ -1463,17 +1468,20 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
|||
if (msglen < 10)
|
||||
{
|
||||
DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
|
||||
abort_cmd (handle);
|
||||
return CCID_DRIVER_ERR_INV_VALUE;
|
||||
}
|
||||
if (buffer[5] != 0)
|
||||
{
|
||||
DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
|
||||
abort_cmd (handle);
|
||||
return CCID_DRIVER_ERR_INV_VALUE;
|
||||
}
|
||||
if (buffer[6] != seqno)
|
||||
{
|
||||
DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
|
||||
seqno, buffer[6]);
|
||||
abort_cmd (handle);
|
||||
return CCID_DRIVER_ERR_INV_VALUE;
|
||||
}
|
||||
|
||||
|
@ -1492,6 +1500,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
|||
if (buffer[0] != expected_type)
|
||||
{
|
||||
DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
|
||||
abort_cmd (handle);
|
||||
return CCID_DRIVER_ERR_INV_VALUE;
|
||||
}
|
||||
|
||||
|
@ -1521,6 +1530,113 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Send an abort sequence and wait until everything settled. */
|
||||
static int
|
||||
abort_cmd (ccid_driver_t handle)
|
||||
{
|
||||
int rc;
|
||||
char dummybuf[8];
|
||||
unsigned char seqno;
|
||||
unsigned char msg[100];
|
||||
size_t msglen;
|
||||
int i;
|
||||
|
||||
if (!handle->idev)
|
||||
{
|
||||
/* I don't know how to send an abort to non-USB devices. */
|
||||
rc = CCID_DRIVER_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
DEBUGOUT ("sending abort sequence\n");
|
||||
/* Send the abort command to the control pipe. Note that we don't
|
||||
need to keep track of sent abort commands because there should
|
||||
never be another thread using the same slot concurrently. */
|
||||
seqno = (handle->seqno & 0xff);
|
||||
rc = usb_control_msg (handle->idev,
|
||||
0x21,/* bmRequestType: host-to-device,
|
||||
class specific, to interface. */
|
||||
1, /* ABORT */
|
||||
(seqno << 8 | 0 /* slot */),
|
||||
handle->ifc_no,
|
||||
dummybuf, 0,
|
||||
1000 /* ms timeout */);
|
||||
if (rc < 0)
|
||||
{
|
||||
DEBUGOUT_1 ("usb_control_msg error: %s\n", strerror (errno));
|
||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
/* Now send the abort command to the bulk out pipe using the same
|
||||
SEQNO and SLOT. */
|
||||
msg[0] = PC_to_RDR_Abort;
|
||||
msg[5] = 0; /* slot */
|
||||
msg[6] = seqno;
|
||||
msg[7] = 0; /* RFU */
|
||||
msg[8] = 0; /* RFU */
|
||||
msg[9] = 0; /* RFU */
|
||||
msglen = 10;
|
||||
set_msg_len (msg, 0);
|
||||
handle->seqno++; /* Bumb up for the next use. */
|
||||
|
||||
DEBUGOUT ("sending");
|
||||
for (i=0; i < msglen; i++)
|
||||
DEBUGOUT_CONT_1 (" %02X", msg[i]);
|
||||
DEBUGOUT_LF ();
|
||||
rc = usb_bulk_write (handle->idev,
|
||||
handle->ep_bulk_out,
|
||||
(char*)msg, msglen,
|
||||
5000 /* ms timeout */);
|
||||
if (rc == msglen)
|
||||
rc = 0;
|
||||
else if (rc == -1)
|
||||
DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", strerror (errno));
|
||||
else
|
||||
DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Wait for the expected response. */
|
||||
do
|
||||
{
|
||||
rc = usb_bulk_read (handle->idev,
|
||||
handle->ep_bulk_in,
|
||||
(char*)msg, sizeof msg,
|
||||
5000 /*ms timeout*/);
|
||||
if (rc < 0)
|
||||
{
|
||||
DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n",
|
||||
strerror (errno));
|
||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||
}
|
||||
msglen = rc;
|
||||
|
||||
if (msglen < 10)
|
||||
{
|
||||
DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n",
|
||||
(unsigned int)msglen);
|
||||
return CCID_DRIVER_ERR_INV_VALUE;
|
||||
}
|
||||
if (msg[5] != 0)
|
||||
{
|
||||
DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
|
||||
return CCID_DRIVER_ERR_INV_VALUE;
|
||||
}
|
||||
|
||||
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n",
|
||||
msg[7], msg[8], msg[9]);
|
||||
if (CCID_COMMAND_FAILED (msg))
|
||||
print_command_failed (msg);
|
||||
}
|
||||
while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);
|
||||
|
||||
DEBUGOUT ("sending abort sequence succeeded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Note that this function won't return the error codes NO_CARD or
|
||||
CARD_INACTIVE. IF RESULT is not NULL, the result from the
|
||||
operation will get returned in RESULT and its length in RESULTLEN.
|
||||
|
|
121
scd/command.c
121
scd/command.c
|
@ -1964,6 +1964,84 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Helper to send the clients a status change notification. */
|
||||
static void
|
||||
send_client_notifications (void)
|
||||
{
|
||||
struct {
|
||||
pid_t pid;
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE handle;
|
||||
#else
|
||||
int signo;
|
||||
#endif
|
||||
} killed[50];
|
||||
int killidx = 0;
|
||||
int kidx;
|
||||
struct server_local_s *sl;
|
||||
|
||||
for (sl=session_list; sl; sl = sl->next_session)
|
||||
{
|
||||
if (sl->event_signal && sl->assuan_ctx)
|
||||
{
|
||||
pid_t pid = assuan_get_pid (sl->assuan_ctx);
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE handle = (void *)sl->event_signal;
|
||||
|
||||
for (kidx=0; kidx < killidx; kidx++)
|
||||
if (killed[kidx].pid == pid
|
||||
&& killed[kidx].handle == handle)
|
||||
break;
|
||||
if (kidx < killidx)
|
||||
log_info ("event %lx (%p) already triggered for client %d\n",
|
||||
sl->event_signal, handle, (int)pid);
|
||||
else
|
||||
{
|
||||
log_info ("triggering event %lx (%p) for client %d\n",
|
||||
sl->event_signal, handle, (int)pid);
|
||||
if (!SetEvent (handle))
|
||||
log_error ("SetEvent(%lx) failed: %s\n",
|
||||
sl->event_signal, w32_strerror (-1));
|
||||
if (killidx < DIM (killed))
|
||||
{
|
||||
killed[killidx].pid = pid;
|
||||
killed[killidx].handle = handle;
|
||||
killidx++;
|
||||
}
|
||||
}
|
||||
#else /*!HAVE_W32_SYSTEM*/
|
||||
int signo = sl->event_signal;
|
||||
|
||||
if (pid != (pid_t)(-1) && pid && signo > 0)
|
||||
{
|
||||
for (kidx=0; kidx < killidx; kidx++)
|
||||
if (killed[kidx].pid == pid
|
||||
&& killed[kidx].signo == signo)
|
||||
break;
|
||||
if (kidx < killidx)
|
||||
log_info ("signal %d already sent to client %d\n",
|
||||
signo, (int)pid);
|
||||
else
|
||||
{
|
||||
log_info ("sending signal %d to client %d\n",
|
||||
signo, (int)pid);
|
||||
kill (pid, signo);
|
||||
if (killidx < DIM (killed))
|
||||
{
|
||||
killed[killidx].pid = pid;
|
||||
killed[killidx].signo = signo;
|
||||
killidx++;
|
||||
}
|
||||
}
|
||||
#endif /*!HAVE_W32_SYSTEM*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This is the core of scd_update_reader_status_file but the caller
|
||||
needs to take care of the locking. */
|
||||
static void
|
||||
|
@ -1985,12 +2063,17 @@ update_reader_status_file (int set_card_removed_flag)
|
|||
{
|
||||
struct slot_status_s *ss = slot_table + idx;
|
||||
struct server_local_s *sl;
|
||||
int sw_apdu;
|
||||
|
||||
if (!ss->valid || ss->slot == -1)
|
||||
continue; /* Not valid or reader not yet open. */
|
||||
|
||||
if ( apdu_get_status (ss->slot, 0, &status, &changed) )
|
||||
continue; /* Get status failed. */
|
||||
sw_apdu = apdu_get_status (ss->slot, 0, &status, &changed);
|
||||
if (sw_apdu)
|
||||
{
|
||||
/* Get status failed. Ignore that. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ss->any || ss->status != status || ss->changed != changed )
|
||||
{
|
||||
|
@ -1998,8 +2081,10 @@ update_reader_status_file (int set_card_removed_flag)
|
|||
char templ[50];
|
||||
FILE *fp;
|
||||
|
||||
log_info ("updating status of slot %d to 0x%04X\n",
|
||||
ss->slot, status);
|
||||
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;
|
||||
|
||||
/* FIXME: Should this be IDX instead of ss->slot? This
|
||||
depends on how client sessions will associate the reader
|
||||
|
@ -2065,33 +2150,9 @@ update_reader_status_file (int set_card_removed_flag)
|
|||
update_card_removed (idx, 1);
|
||||
|
||||
ss->any = 1;
|
||||
ss->status = status;
|
||||
ss->changed = changed;
|
||||
|
||||
/* Send a signal to all clients who applied for it. */
|
||||
for (sl=session_list; sl; sl = sl->next_session)
|
||||
if (sl->event_signal && sl->assuan_ctx)
|
||||
{
|
||||
pid_t pid = assuan_get_pid (sl->assuan_ctx);
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE handle = (void *)sl->event_signal;
|
||||
|
||||
log_info ("client pid is %d, triggering event %lx (%p)\n",
|
||||
pid, sl->event_signal, handle);
|
||||
if (!SetEvent (handle))
|
||||
log_error ("SetEvent(%lx) failed: %s\n",
|
||||
sl->event_signal, w32_strerror (-1));
|
||||
#else
|
||||
int signo = sl->event_signal;
|
||||
|
||||
log_info ("client pid is %d, sending signal %d\n",
|
||||
pid, signo);
|
||||
if (pid != (pid_t)(-1) && pid && signo > 0)
|
||||
kill (pid, signo);
|
||||
#endif
|
||||
}
|
||||
|
||||
send_client_notifications ();
|
||||
}
|
||||
|
||||
/* Check whether a disconnect is pending. */
|
||||
|
@ -2104,7 +2165,7 @@ update_reader_status_file (int set_card_removed_flag)
|
|||
{
|
||||
/* FIXME: Use a real timeout. */
|
||||
/* At least one connection and all allow a disconnect. */
|
||||
log_debug ("disconnecting card in slot %d\n", ss->slot);
|
||||
log_info ("disconnecting card in slot %d\n", ss->slot);
|
||||
apdu_disconnect (ss->slot);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue