[PATCH] pcsc pinpad support (part 1)
NIIBE Yutaka
gniibe at fsij.org
Thu Jan 13 05:59:39 CET 2011
This is the replacement of the (part 1) patch.
2011-01-13 11:06, NIIBE Yutaka wrote:
> I am submitting a series of patches for pcsc pinpad support.
[...]
> The first patch is adding pcsc_control handle and two fields to
> reader_table_s structure; verify_ioctl and modify_ioctl which
> correspond to the operations of PIN Verification and PIN Modification.
>
> (In the forthcoming another patch,) the value of verify_ioctl and
> modify_ioctl will be filled using pcsc_control. Since the value is in
> the format of big-endian TLV (Tag-Length-Value), convert_be_u32 is
> added.
This patch includes filling of verify_ioctl and modify_ioctl too.
I don't use convert_be_u32, this time.
Perhaps, check_pcsc_keypad should be more restrictive like
check_ccid_keypad. This implementation just returns "Yes", when the
reader says so.
2011-01-13 NIIBE Yutaka <gniibe at fsij.org>
* pcsc-wrapper.c (load_pcsc_driver): Initialize pcsc_control.
(handle_control): New.
(main): Handle the case 6 of handle_control.
* apdu.c: Include "iso7816.h".
(struct reader_table_s): Add fields verify_ioctl and
modify_ioctl in pcsc.
(CM_IOCTL_GET_FEATURE_REQUEST, FEATURE_VERIFY_PIN_DIRECT)
(FEATURE_MODIFY_PIN_DIRECT): New.
(pcsc_control): New.
(check_pcsc_keypad): New.
(control_pcsc_direct, control_pcsc_wrapped, control_pcsc): New.
(new_reader_slot): Initialize with check_pcsc_keypad,
verify_ioctl and modify_ioctl.
(apdu_open_reader): Initialize pcsc_control.
---
scd/apdu.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++-
scd/pcsc-wrapper.c | 50 +++++++++++-
2 files changed, 277 insertions(+), 5 deletions(-)
diff --git a/scd/apdu.c b/scd/apdu.c
index 80c933e..4942d05 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -61,6 +61,7 @@
#include "apdu.h"
#include "ccid-driver.h"
+#include "iso7816.h"
/* Due to conflicting use of threading libraries we usually can't link
@@ -117,6 +118,8 @@ struct reader_table_s {
unsigned long context;
unsigned long card;
unsigned long protocol;
+ unsigned long verify_ioctl;
+ unsigned long modify_ioctl;
#ifdef NEED_PCSC_WRAPPER
int req_fd;
int rsp_fd;
@@ -235,6 +238,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
#define PCSC_E_READER_UNAVAILABLE 0x80100017
#define PCSC_W_REMOVED_CARD 0x80100069
+#define CM_IOCTL_GET_FEATURE_REQUEST (0x42000000 + 3400)
+#define FEATURE_VERIFY_PIN_DIRECT 0x06
+#define FEATURE_MODIFY_PIN_DIRECT 0x07
+
+
/* The PC/SC error is defined as a long as per specs. Due to left
shifts bit 31 will get sign extended. We use this mask to fix
it. */
@@ -303,6 +311,13 @@ long (* DLSTDCALL pcsc_transmit) (unsigned long card,
unsigned long *recv_len);
long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
+long (* DLSTDCALL pcsc_control) (unsigned long card,
+ unsigned long control_code,
+ const void *send_buffer,
+ unsigned long send_len,
+ void *recv_buffer,
+ unsigned long recv_len,
+ unsigned long *bytes_returned);
/* Prototypes. */
@@ -311,6 +326,8 @@ static int reset_pcsc_reader (int slot);
static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
unsigned int *status,
unsigned int *changed);
+static int check_pcsc_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen);
@@ -354,7 +371,7 @@ new_reader_slot (void)
reader_table[reader].reset_reader = NULL;
reader_table[reader].get_status_reader = NULL;
reader_table[reader].send_apdu_reader = NULL;
- reader_table[reader].check_keypad = NULL;
+ reader_table[reader].check_keypad = check_pcsc_keypad;
reader_table[reader].dump_status_reader = NULL;
reader_table[reader].set_progress_cb = NULL;
@@ -367,6 +384,8 @@ new_reader_slot (void)
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif
+ reader_table[reader].pcsc.verify_ioctl = 0;
+ reader_table[reader].pcsc.modify_ioctl = 0;
return reader;
}
@@ -1168,6 +1187,150 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifndef NEED_PCSC_WRAPPER
static int
+control_pcsc_direct (int slot, unsigned long ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, size_t *buflen)
+{
+ long err;
+
+ err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
+ cntlbuf, len, buffer, *buflen, buflen);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+control_pcsc_wrapped (int slot, unsigned long ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, size_t *buflen)
+{
+ long err = PCSC_E_NOT_TRANSACTED;
+ reader_table_t slotp;
+ unsigned char msgbuf[9];
+ int i, n;
+ size_t full_len;
+
+ slotp = reader_table + slot;
+
+ msgbuf[0] = 0x06; /* CONTROL command. */
+ msgbuf[1] = ((len + 4) >> 24);
+ msgbuf[2] = ((len + 4) >> 16);
+ msgbuf[3] = ((len + 4) >> 8);
+ msgbuf[4] = ((len + 4) );
+ msgbuf[5] = (ioctl_code >> 24);
+ msgbuf[6] = (ioctl_code >> 16);
+ msgbuf[7] = (ioctl_code >> 8);
+ msgbuf[8] = (ioctl_code );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 9)
+ || writen (slotp->pcsc.req_fd, cntlbuf, len))
+ {
+ log_error ("error sending PC/SC CONTROL 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 CONTROL 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 = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ 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 CONTROL 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 = PCSC_E_INVALID_VALUE;
+ }
+ /* We need to read any rest of the response, to keep the
+ protocol running. */
+ 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 CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ full_len -= n;
+ }
+
+ if (!err)
+ return 0;
+
+ 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 pcsc_error_to_sw (err);
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+
+
+/* Do some control with the value of IOCTL_CODE to the card inserted
+ to SLOT. Input buffer is specified by CNTLBUF of length LEN.
+ Output buffer is specified by BUFFER of length *BUFLEN, and the
+ actual output size will be stored at BUFLEN. Returns: A status word.
+ This routine is used for PIN pad input support. */
+static int
+control_pcsc (int slot, unsigned long ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, size_t *buflen)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return control_pcsc_wrapped (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#else
+ return control_pcsc_direct (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#endif
+}
+
+
+#ifndef NEED_PCSC_WRAPPER
+static int
close_pcsc_reader_direct (int slot)
{
pcsc_release_context (reader_table[slot].pcsc.context);
@@ -1802,6 +1965,66 @@ open_pcsc_reader (const char *portstr)
}
+/* Check whether the reader supports the ISO command code COMMAND
+ on the keypad. Return 0 on success. */
+static int
+check_pcsc_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ unsigned char buf[256];
+ size_t len = 256;
+ int sw;
+
+ (void)pin_mode;
+ (void)pinlen_min;
+ (void)pinlen_max;
+ (void)pin_padlen;
+
+ check_again:
+ if (command == ISO7816_VERIFY)
+ {
+ if (reader_table[slot].pcsc.verify_ioctl == (unsigned long)-1)
+ return SW_NOT_SUPPORTED;
+ else if (reader_table[slot].pcsc.verify_ioctl != 0)
+ return 0; /* Success */
+ }
+ else if (command == ISO7816_CHANGE_REFERENCE_DATA)
+ {
+ if (reader_table[slot].pcsc.modify_ioctl == (unsigned long)-1)
+ return SW_NOT_SUPPORTED;
+ else if (reader_table[slot].pcsc.modify_ioctl != 0)
+ return 0; /* Success */
+ }
+ else
+ return SW_NOT_SUPPORTED;
+
+ reader_table[slot].pcsc.verify_ioctl = (unsigned long)-1;
+ reader_table[slot].pcsc.modify_ioctl = (unsigned long)-1;
+
+ sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
+ if (sw)
+ return SW_NOT_SUPPORTED;
+ else
+ {
+ unsigned char *p = buf;
+
+ while (p < buf + len)
+ {
+ unsigned char code = *p++;
+
+ p++; /* Skip length */
+ if (code == FEATURE_VERIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.verify_ioctl
+ = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ else if (code == FEATURE_MODIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.modify_ioctl
+ = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ }
+ }
+
+ goto check_again;
+}
#ifdef HAVE_LIBUSB
/*
@@ -2438,6 +2661,7 @@ apdu_open_reader (const char *portstr)
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
@@ -2450,12 +2674,13 @@ apdu_open_reader (const char *portstr)
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
+ || !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
log_error ("apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
@@ -2467,7 +2692,8 @@ apdu_open_reader (const char *portstr)
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
- !!pcsc_set_timeout );
+ !!pcsc_set_timeout,
+ !!pcsc_control );
dlclose (handle);
return -1;
}
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
index a7b2198..9379655 100644
--- a/scd/pcsc-wrapper.c
+++ b/scd/pcsc-wrapper.c
@@ -178,6 +178,13 @@ long (* pcsc_transmit) (unsigned long card,
unsigned long *recv_len);
long (* pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
+long (* pcsc_control) (unsigned long card,
+ unsigned long control_code,
+ const void *send_buffer,
+ unsigned long send_len,
+ void *recv_buffer,
+ unsigned long recv_len,
+ unsigned long *bytes_returned);
@@ -335,6 +342,7 @@ load_pcsc_driver (const char *libname)
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
@@ -347,13 +355,14 @@ load_pcsc_driver (const char *libname)
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
+ || !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
fprintf (stderr,
"apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
@@ -365,7 +374,8 @@ load_pcsc_driver (const char *libname)
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
- !!pcsc_set_timeout );
+ !!pcsc_set_timeout,
+ !!pcsc_control );
dlclose (handle);
exit (1);
}
@@ -721,6 +731,38 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
}
+/* Handle a control request. The argument is expected to be a buffer
+ which contains CONTROL_CODE (4-byte) and INPUT_BYTES.
+ */
+static void
+handle_control (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ unsigned long ioctl_code;
+ unsigned long recv_len = 1024;
+ unsigned char buffer[1024];
+
+ if (arglen < 4)
+ bad_request ("CONTROL");
+
+ ioctl_code = (argbuf[0] << 24) | (argbuf[1] << 16) | (argbuf[2] << 8) | argbuf[3];
+ argbuf += 4;
+ arglen -= 4;
+
+ recv_len = sizeof (buffer);
+ err = pcsc_control (pcsc_card, ioctl_code, argbuf, arglen,
+ buffer, recv_len, &recv_len);
+ if (err)
+ {
+ if (verbose)
+ fprintf (stderr, PGM": pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+ request_succeeded (buffer, recv_len);
+}
+
static void
print_version (int with_help)
@@ -832,6 +874,10 @@ main (int argc, char **argv)
handle_reset (argbuffer, arglen);
break;
+ case 6:
+ handle_control (argbuffer, arglen);
+ break;
+
default:
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
exit (1);
--
1.7.2.3
More information about the Gnupg-devel
mailing list