From 26b4a012e3eb3a6ce79a1e53f7cdfbbdf8c8e8f5 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Mon, 28 Nov 2011 16:16:38 +0900 Subject: [PATCH] PC/SC pinpad support. Before this change, it is layered like following: iso7816_verify iso7816_verify_kp apdu_send_simple, apdu_send_simple_kp ... After this change, it will be layered like: iso7816_verify iso7816_verify_kp apdu_send_simple apdu_keypad_verify ... and apdu_send_simple_kp will be deprecated. For PC/SC API, we use: SCardControl API to compose CCID PC_to_RDR_Secure message SCardTransmit API to compose CCID PC_to_RDR_XfrBlock message Considering the support of PC/SC, we have nothing to share between _kp version of iso7816_* and no _kp version. --- scd/ChangeLog | 35 +++++ scd/apdu.c | 362 ++++++++++++++++++++++++++++++++++++++++++++- scd/apdu.h | 3 + scd/app-dinsig.c | 2 +- scd/app-nks.c | 2 +- scd/app-openpgp.c | 4 +- scd/iso7816.c | 23 ++- scd/iso7816.h | 4 +- scd/pcsc-wrapper.c | 50 ++++++- 9 files changed, 460 insertions(+), 25 deletions(-) diff --git a/scd/ChangeLog b/scd/ChangeLog index 45c9c7c0c..9f991cec0 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,38 @@ +2011-11-28 Niibe Yutaka + + * iso7816.h (iso7816_verify_kp): Remove arguments of CHV and CHVLEN. + + * iso7816.c (iso7816_verify_kp): Call apdu_keypad_verify. Only + handle thecase with PININFO. + (iso7816_verify): Call apdu_send_simple. + + * app-openpgp.c (verify_a_chv, verify_chv3): Follow the change of + iso7816_verify_kp. + + * app-nks.c (verify_pin): Likewise. + + * app-dinsig.c (verify_pin): Likewise. + + * apdu.c: Include "iso7816.h". + (struct reader_table_s): New memeber function keypad_verify. + 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. + (control_pcsc_direct, control_pcsc_wrapped, control_pcsc) + (check_pcsc_keypad, pcsc_keypad_verify): New. + (ccid_keypad_verify, apdu_keypad_verify): New. + (new_reader_slot): Initialize with check_pcsc_keypad, + pcsc_keypad_verify, verify_ioctl and modify_ioctl. + (open_ct_reader): Initialize keypad_verify with NULL. + (open_ccid_reader): Initialize keypad_verify. + (open_rapdu_reader): Initialize keypad_verify with NULL. + (apdu_open_reader): Initialize pcsc_control. + + * pcsc-wrapper.c (load_pcsc_driver): Initialize pcsc_control. + (handle_control): New. + (main): Handle the case 6 of handle_control. + 2011-08-10 Werner Koch * command.c (cmd_killscd): Use the new assuan force close flag diff --git a/scd/apdu.c b/scd/apdu.c index ac563adef..866ebb916 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -62,6 +62,7 @@ #include "apdu.h" #include "ccid-driver.h" +#include "iso7816.h" /* Due to conflicting use of threading libraries we usually can't link @@ -110,6 +111,7 @@ struct reader_table_s { int (*check_keypad)(int, int, int, int, int, int); void (*dump_status_reader)(int); int (*set_progress_cb)(int, gcry_handler_progress_t, void*); + int (*keypad_verify)(int, int, int, int, int, struct pininfo_s *); struct { ccid_driver_t handle; @@ -118,6 +120,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; @@ -236,6 +240,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. */ @@ -304,6 +313,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); /* Flag set if PC/SC returned the no-service error. */ static int pcsc_no_service; @@ -315,6 +331,10 @@ 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); +static int pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1, + struct pininfo_s *pininfo); @@ -358,9 +378,10 @@ 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; + reader_table[reader].keypad_verify = pcsc_keypad_verify; reader_table[reader].used = 1; reader_table[reader].any_status = 0; @@ -371,6 +392,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; } @@ -645,6 +668,7 @@ open_ct_reader (int port) reader_table[reader].send_apdu_reader = ct_send_apdu; reader_table[reader].check_keypad = NULL; reader_table[reader].dump_status_reader = ct_dump_reader_status; + reader_table[reader].keypad_verify = NULL; dump_reader_status (reader); return reader; @@ -1170,6 +1194,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) @@ -1808,6 +1976,138 @@ 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; +} + + +#define PIN_VERIFY_STRUCTURE_SIZE 23 +static int +pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1, + struct pininfo_s *pininfo) +{ + int sw; + unsigned char *pin_verify; + unsigned long len = PIN_VERIFY_STRUCTURE_SIZE; + unsigned char result[2]; + size_t resultlen = 2; + + if (!reader_table[slot].atrlen + && (sw = reset_pcsc_reader (slot))) + return sw; + + if (pininfo->mode != 1) + return SW_NOT_SUPPORTED; + + if (pininfo->padlen != 0) + return SW_NOT_SUPPORTED; + + if (!pininfo->minlen) + pininfo->minlen = 1; + if (!pininfo->maxlen) + pininfo->maxlen = 25; + + /* Note that the 25 is the maximum value the SPR532 allows. */ + if (pininfo->minlen < 1 || pininfo->minlen > 25 + || pininfo->maxlen < 1 || pininfo->maxlen > 25 + || pininfo->minlen > pininfo->maxlen) + return SW_HOST_INV_VALUE; + + pin_verify = xtrymalloc (len); + if (!pin_verify) + return SW_HOST_OUT_OF_CORE; + + pin_verify[0] = 0x00; /* bTimerOut */ + pin_verify[1] = 0x00; /* bTimerOut2 */ + pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */ + pin_verify[3] = 0x00; /* bmPINBlockString */ + pin_verify[4] = 0x00; /* bmPINLengthFormat */ + pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */ + pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */ + pin_verify[7] = 0x02; /* bEntryValidationCondition: Validation key pressed */ + if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen) + pin_verify[7] |= 0x01; /* Max size reached. */ + pin_verify[8] = 0xff; /* bNumberMessage: Default */ + pin_verify[9] = 0x09; /* wLangId: 0x0409: US English */ + pin_verify[10] = 0x04; /* wLangId: 0x0409: US English */ + pin_verify[11] = 0x00; /* bMsgIndex */ + pin_verify[12] = 0x00; /* bTeoPrologue[0] */ + pin_verify[13] = 0x00; /* bTeoPrologue[1] */ + pin_verify[14] = 0x00; /* bTeoPrologue[2] */ + pin_verify[15] = 0x04; /* ulDataLength */ + pin_verify[16] = 0x00; /* ulDataLength */ + pin_verify[17] = 0x00; /* ulDataLength */ + pin_verify[18] = 0x00; /* ulDataLength */ + pin_verify[19] = class; /* abData[0] */ + pin_verify[20] = ins; /* abData[1] */ + pin_verify[21] = p0; /* abData[2] */ + pin_verify[22] = p1; /* abData[3] */ + + 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]; + return sw; +} #ifdef HAVE_LIBUSB /* @@ -1945,6 +2245,35 @@ check_ccid_keypad (int slot, int command, int pin_mode, } +static int +ccid_keypad_verify (int slot, int class, int ins, int p0, int p1, + struct pininfo_s *pininfo) +{ + unsigned char apdu[4]; + int err, sw; + unsigned char result[2]; + size_t resultlen = 2; + + apdu[0] = class; + apdu[1] = ins; + apdu[2] = p0; + apdu[3] = p1; + err = ccid_transceive_secure (reader_table[slot].ccid.handle, + apdu, sizeof apdu, + pininfo->mode, pininfo->minlen, pininfo->maxlen, + pininfo->padlen, + result, 2, &resultlen); + if (err) + return err; + + if (resultlen < 2) + return SW_HOST_INCOMPLETE_CARD_RESPONSE; + + sw = (result[resultlen-2] << 8) | result[resultlen-1]; + return sw; +} + + /* Open the reader and try to read an ATR. */ static int open_ccid_reader (const char *portstr) @@ -1989,6 +2318,7 @@ open_ccid_reader (const char *portstr) reader_table[slot].check_keypad = check_ccid_keypad; reader_table[slot].dump_status_reader = dump_ccid_reader_status; reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader; + reader_table[slot].keypad_verify = ccid_keypad_verify; /* Our CCID reader code does not support T=0 at all, thus reset the flag. */ reader_table[slot].is_t0 = 0; @@ -2281,6 +2611,7 @@ open_rapdu_reader (int portno, reader_table[slot].send_apdu_reader = my_rapdu_send_apdu; reader_table[slot].check_keypad = NULL; reader_table[slot].dump_status_reader = NULL; + reader_table[slot].keypad_verify = NULL; dump_reader_status (slot); rapdu_msg_release (msg); @@ -2461,6 +2792,7 @@ apdu_open_reader (const char *portstr, int *r_no_service) 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 @@ -2473,12 +2805,13 @@ apdu_open_reader (const char *portstr, int *r_no_service) || !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, @@ -2490,7 +2823,8 @@ apdu_open_reader (const char *portstr, int *r_no_service) !!pcsc_begin_transaction, !!pcsc_end_transaction, !!pcsc_transmit, - !!pcsc_set_timeout ); + !!pcsc_set_timeout, + !!pcsc_control ); dlclose (handle); return -1; } @@ -2894,6 +3228,28 @@ apdu_check_keypad (int slot, int command, int pin_mode, } +int +apdu_keypad_verify (int slot, int class, int ins, int p0, int p1, int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen) +{ + struct pininfo_s pininfo; + + pininfo.mode = pin_mode; + pininfo.minlen = pinlen_min; + pininfo.maxlen = pinlen_max; + pininfo.padlen = pin_padlen; + + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + if (reader_table[slot].keypad_verify) + return reader_table[slot].keypad_verify (slot, class, ins, p0, p1, + &pininfo); + else + return SW_HOST_NOT_SUPPORTED; +} + + /* Dispatcher for the actual send_apdu function. Note, that this function should be called in locked state. */ static int diff --git a/scd/apdu.h b/scd/apdu.h index 7c0188782..4dff9eb95 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -114,6 +114,9 @@ int apdu_get_status (int slot, int hang, unsigned int *status, unsigned int *changed); int apdu_check_keypad (int slot, int command, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen); +int apdu_keypad_verify (int slot, int class, int ins, int p0, int p1, + int pin_mode, int pinlen_min, int pinlen_max, + int pin_padlen); int apdu_send_simple (int slot, int extended_mode, int class, int ins, int p0, int p1, int lc, const char *data); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 30beb8efb..50db78e1f 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -304,7 +304,7 @@ verify_pin (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x81, "", 0, &pininfo); + rc = iso7816_verify_kp (app->slot, 0x81, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } diff --git a/scd/app-nks.c b/scd/app-nks.c index c1b2aa376..28ccb9af7 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -803,7 +803,7 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo); + rc = iso7816_verify_kp (app->slot, pwid, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index eb0b4f029..d7efad562 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1550,7 +1550,7 @@ verify_a_chv (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x80+chvno, "", 0, &pininfo); + rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); @@ -1730,7 +1730,7 @@ verify_chv3 (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x83, "", 0, &pininfo); + rc = iso7816_verify_kp (app->slot, 0x83, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } diff --git a/scd/iso7816.c b/scd/iso7816.c index 318fec8a2..1238552f6 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -281,22 +281,16 @@ iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo) /* Perform a VERIFY command on SLOT using the card holder verification - vector CHVNO with a CHV of lenght CHVLEN. With PININFO non-NULL - the keypad of the reader will be used. Returns 0 on success. */ + vector CHVNO. With PININFO non-NULL the keypad of the reader will + be used. Returns 0 on success. */ gpg_error_t -iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen, - iso7816_pininfo_t *pininfo) +iso7816_verify_kp (int slot, int chvno, iso7816_pininfo_t *pininfo) { int sw; - if (pininfo && pininfo->mode) - sw = apdu_send_simple_kp (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv, - pininfo->mode, - pininfo->minlen, - pininfo->maxlen, - pininfo->padlen); - else - sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); + sw = apdu_keypad_verify (slot, 0x00, CMD_VERIFY, 0, chvno, + pininfo->mode, pininfo->minlen, pininfo->maxlen, + pininfo->padlen); return map_sw (sw); } @@ -305,7 +299,10 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen, gpg_error_t iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) { - return iso7816_verify_kp (slot, chvno, chv, chvlen, NULL); + int sw; + + sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); + return map_sw (sw); } /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder diff --git a/scd/iso7816.h b/scd/iso7816.h index a37759dbe..58e81d458 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -63,9 +63,7 @@ gpg_error_t iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo); gpg_error_t iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen); -gpg_error_t iso7816_verify_kp (int slot, - int chvno, const char *chv, size_t chvlen, - iso7816_pininfo_t *pininfo); +gpg_error_t iso7816_verify_kp (int slot, int chvno, iso7816_pininfo_t *pininfo); gpg_error_t iso7816_change_reference_data (int slot, int chvno, const char *oldchv, size_t oldchvlen, const char *newchv, size_t newchvlen); diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c index ee974ac7b..73b25f4b3 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); } @@ -720,6 +730,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) @@ -831,6 +873,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);