diff options
Diffstat (limited to 'scd')
-rw-r--r-- | scd/ChangeLog | 55 | ||||
-rw-r--r-- | scd/apdu.c | 176 | ||||
-rw-r--r-- | scd/apdu.h | 9 | ||||
-rw-r--r-- | scd/app-dinsig.c | 41 | ||||
-rw-r--r-- | scd/app-nks.c | 11 | ||||
-rw-r--r-- | scd/app-openpgp.c | 9 | ||||
-rw-r--r-- | scd/app-p15.c | 13 | ||||
-rw-r--r-- | scd/ccid-driver.c | 64 | ||||
-rw-r--r-- | scd/ccid-driver.h | 7 | ||||
-rw-r--r-- | scd/iso7816.c | 115 | ||||
-rw-r--r-- | scd/iso7816.h | 35 | ||||
-rw-r--r-- | scd/scdaemon.c | 6 | ||||
-rw-r--r-- | scd/scdaemon.h | 17 |
13 files changed, 461 insertions, 97 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog index ccea117dd..008d84080 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,8 +1,63 @@ +2005-11-23 Werner Koch <[email protected]> + + * app-nks.c (verify_pin): Give a special error message for a Nullpin. + +2005-10-29 Werner Koch <[email protected]> + + * ccid-driver.c (send_escape_cmd): New args RESULT, RESULTLEN and + RESULTMAX. Changed all callers. + (ccid_transceive_escape): New. + +2005-10-27 Werner Koch <[email protected]> + + * apdu.c [__CYGWIN__]: Make cygwin environment similar to _WIN32. + Suggested by John P. Clizbe. + * scdaemon.c [__CYGWIN__]: Set default PC/SC driver to winscard.dll. + +2005-10-19 Werner Koch <[email protected]> + + * ccid-driver.h (CCID_DRIVER_ERR_NO_KEYPAD): New. + * apdu.h (SW_HOST_NO_KEYPAD): New. + * iso7816.h (struct iso7816_pininfo_s): New. + * iso7816.c (map_sw): Support new code. + (iso7816_check_keypad): New. + (iso7816_verify_kp, iso7816_change_reference_data_kp) + (iso7816_reset_retry_counter_kp): New. Extended versions of the + original functions. + * apdu.c (host_sw_string): Support new code. + (reader_table_s): New field CHECK_KEYPAD. + (new_reader_slot, open_ct_reader, open_pcsc_reader) + (open_ccid_reader, open_rapdu_reader): Initialize it. + (check_ccid_keypad): New. + (apdu_check_keypad): New. + (apdu_send_le): Factored all code out to ... + (send_le): .. new. Takes an additional arg; changed all callers + of the orginal function to use this one with a NULL for the new + arg. + (apdu_send_simple_kp): New. + (ct_send_apdu, pcsc_send_apdu, my_rapdu_send_apdu) + (send_apdu_ccid): New arg PININFO. + (send_apdu_ccid): Use the new arg. + + * scdaemon.c: New option --disable-keypad. + 2005-10-08 Marcus Brinkmann <[email protected]> * Makefile.am (scdaemon_LDADD): Add ../gl/libgnu.a after ../common/libcommon.a. +2005-09-20 Werner Koch <[email protected]> + + * app-dinsig.c (verify_pin): Try ISO 9564 BCD encoding. + + * iso7816.c (iso7816_select_application): Add arg FLAGS. Changed + all callers to pass 0. + * app-openpgp.c (app_select_openpgp): But this one requires a + special flag. + + * app-p15.c (app_select_p15): Don't use select application for the + BELPIC. + 2005-09-09 Werner Koch <[email protected]> * pcsc-wrapper.c (main): Removed bogus free. diff --git a/scd/apdu.c b/scd/apdu.c index 678ea12d3..f59d832d4 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -66,10 +66,10 @@ #include "ccid-driver.h" -/* To to conflicting use of threading libraries we usually can't link +/* Due to conflicting use of threading libraries we usually can't link against libpcsclite. Instead we use a wrapper program. */ #ifdef USE_GNU_PTH -#ifndef HAVE_W32_SYSTEM +#if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__) #define NEED_PCSC_WRAPPER 1 #endif #endif @@ -78,7 +78,7 @@ #define MAX_READER 4 /* Number of readers we support concurrently. */ -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) #define DLSTDCALL __stdcall #else #define DLSTDCALL @@ -90,6 +90,14 @@ #define MAX_OPEN_FDS 20 #endif +/* Helper to pass patrameters related to keypad based operations. */ +struct pininfo_s +{ + int mode; + int minlen; + int maxlen; + int padlen; +}; /* A structure to collect information pertaining to one reader slot. */ @@ -103,7 +111,8 @@ struct reader_table_s { int (*reset_reader)(int); int (*get_status_reader)(int, unsigned int *); int (*send_apdu_reader)(int,unsigned char *,size_t, - unsigned char *, size_t *); + unsigned char *, size_t *, struct pininfo_s *); + int (*check_keypad)(int, int, int, int, int, int); void (*dump_status_reader)(int); struct { @@ -320,6 +329,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].dump_status_reader = NULL; reader_table[reader].used = 1; @@ -372,6 +382,7 @@ host_sw_string (long err) case SW_HOST_GENERAL_ERROR: return "general error"; case SW_HOST_NO_READER: return "no reader"; case SW_HOST_ABORTED: return "aborted"; + case SW_HOST_NO_KEYPAD: return "no keypad"; default: return "unknown host status error"; } } @@ -533,7 +544,7 @@ ct_get_status (int slot, unsigned int *status) set to BUFLEN. Returns: CT API error code. */ static int ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { int rc; unsigned char dad[1], sad[1]; @@ -596,6 +607,7 @@ open_ct_reader (int port) reader_table[reader].reset_reader = reset_ct_reader; reader_table[reader].get_status_reader = ct_get_status; 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; dump_reader_status (reader); @@ -1082,7 +1094,8 @@ pcsc_get_status (int slot, unsigned int *status) set to BUFLEN. Returns: CT API error code. */ static int pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, + struct pininfo_s *pininfo) { #ifdef NEED_PCSC_WRAPPER long err; @@ -1479,6 +1492,7 @@ open_pcsc_reader (const char *portstr) reader_table[slot].reset_reader = reset_pcsc_reader; reader_table[slot].get_status_reader = pcsc_get_status; reader_table[slot].send_apdu_reader = pcsc_send_apdu; + reader_table[slot].check_keypad = NULL; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; /* Read the status so that IS_T0 will be set. */ @@ -1625,6 +1639,7 @@ open_pcsc_reader (const char *portstr) reader_table[slot].reset_reader = reset_pcsc_reader; reader_table[slot].get_status_reader = pcsc_get_status; reader_table[slot].send_apdu_reader = pcsc_send_apdu; + reader_table[slot].check_keypad = NULL; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; /* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */ @@ -1713,7 +1728,8 @@ get_status_ccid (int slot, unsigned int *status) set to BUFLEN. Returns: Internal CCID driver error code. */ static int send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, + struct pininfo_s *pininfo) { long err; size_t maxbuflen; @@ -1727,9 +1743,18 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, log_printhex (" APDU_data:", apdu, apdulen); maxbuflen = *buflen; - err = ccid_transceive (reader_table[slot].ccid.handle, - apdu, apdulen, - buffer, maxbuflen, buflen); + if (pininfo) + err = ccid_transceive_secure (reader_table[slot].ccid.handle, + apdu, apdulen, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen, + buffer, maxbuflen, buflen); + else + err = ccid_transceive (reader_table[slot].ccid.handle, + apdu, apdulen, + buffer, maxbuflen, buflen); if (err) log_error ("ccid_transceive failed: (0x%lx)\n", err); @@ -1737,6 +1762,24 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, return err; } + +/* Check whether the CCID reader supports the ISO command code COMMAND + on the keypad. Return 0 on success. For a description of the pin + parameters, see ccid-driver.c */ +static int +check_ccid_keypad (int slot, int command, int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen) +{ + unsigned char apdu[] = { 0, 0, 0, 0x81 }; + + apdu[1] = command; + return ccid_transceive_secure (reader_table[slot].ccid.handle, + apdu, sizeof apdu, + pin_mode, pinlen_min, pinlen_max, pin_padlen, + NULL, 0, NULL); +} + + /* Open the reader and try to read an ATR. */ static int open_ccid_reader (const char *portstr) @@ -1776,6 +1819,7 @@ open_ccid_reader (const char *portstr) reader_table[slot].reset_reader = reset_ccid_reader; reader_table[slot].get_status_reader = get_status_ccid; reader_table[slot].send_apdu_reader = send_apdu_ccid; + reader_table[slot].check_keypad = check_ccid_keypad; reader_table[slot].dump_status_reader = dump_ccid_reader_status; dump_reader_status (slot); @@ -1932,7 +1976,8 @@ my_rapdu_get_status (int slot, unsigned int *status) set to BUFLEN. Returns: APDU error code. */ static int my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, + struct pininfo_s *pininfo) { int err; reader_table_t slotp; @@ -2063,6 +2108,7 @@ open_rapdu_reader (int portno, reader_table[slot].reset_reader = reset_rapdu_reader; reader_table[slot].get_status_reader = my_rapdu_get_status; reader_table[slot].send_apdu_reader = my_rapdu_send_apdu; + reader_table[slot].check_keypad = NULL; reader_table[slot].dump_status_reader = NULL; dump_reader_status (slot); @@ -2198,28 +2244,28 @@ apdu_open_reader (const char *portstr) pcsc_establish_context = dlsym (handle, "SCardEstablishContext"); pcsc_release_context = dlsym (handle, "SCardReleaseContext"); pcsc_list_readers = dlsym (handle, "SCardListReaders"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_list_readers) pcsc_list_readers = dlsym (handle, "SCardListReadersA"); #endif pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_get_status_change) pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA"); #endif pcsc_connect = dlsym (handle, "SCardConnect"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_connect) pcsc_connect = dlsym (handle, "SCardConnectA"); #endif pcsc_reconnect = dlsym (handle, "SCardReconnect"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_reconnect) pcsc_reconnect = dlsym (handle, "SCardReconnectA"); #endif pcsc_disconnect = dlsym (handle, "SCardDisconnect"); pcsc_status = dlsym (handle, "SCardStatus"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_status) pcsc_status = dlsym (handle, "SCardStatusA"); #endif @@ -2492,11 +2538,30 @@ apdu_get_status (int slot, int hang, } +/* Check whether the reader supports the ISO command code COMMAND on + the keypad. Return 0 on success. For a description of the pin + parameters, see ccid-driver.c */ +int +apdu_check_keypad (int slot, int command, int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen) +{ + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + if (reader_table[slot].check_keypad) + return reader_table[slot].check_keypad (slot, command, + pin_mode, pinlen_min, pinlen_max, + pin_padlen); + 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 send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; @@ -2504,24 +2569,20 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, if (reader_table[slot].send_apdu_reader) return reader_table[slot].send_apdu_reader (slot, apdu, apdulen, - buffer, buflen); + buffer, buflen, pininfo); else return SW_HOST_NOT_SUPPORTED; } -/* Send an APDU to the card in SLOT. The APDU is created from all - given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 - for LC won't sent this field and the data field; in this case DATA - must also be passed as NULL. The return value is the status word - or -1 for an invalid SLOT or other non card related error. If - RETBUF is not NULL, it will receive an allocated buffer with the - returned data. The length of that data will be put into - *RETBUFLEN. The caller is reponsible for releasing the buffer even - in case of errors. */ -int -apdu_send_le(int slot, int class, int ins, int p0, int p1, - int lc, const char *data, int le, - unsigned char **retbuf, size_t *retbuflen) + +/* Core APDU trabceiver function. Parameters are described at + apdu_send_le with the exception of PININFO which indicates keypad + related operations if not NULL. */ +static int +send_le (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, int le, + unsigned char **retbuf, size_t *retbuflen, + struct pininfo_s *pininfo) { #define RESULTLEN 256 unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in @@ -2570,7 +2631,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, /* As safeguard don't pass any garbage from the stack to the driver. */ memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) failed: %s\n", @@ -2638,7 +2699,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) for get response failed: %s\n", @@ -2704,6 +2765,27 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, } /* Send an APDU to the card in SLOT. The APDU is created from all + given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 + for LC won't sent this field and the data field; in this case DATA + must also be passed as NULL. The return value is the status word + or -1 for an invalid SLOT or other non card related error. If + RETBUF is not NULL, it will receive an allocated buffer with the + returned data. The length of that data will be put into + *RETBUFLEN. The caller is reponsible for releasing the buffer even + in case of errors. */ +int +apdu_send_le(int slot, int class, int ins, int p0, int p1, + int lc, const char *data, int le, + unsigned char **retbuf, size_t *retbuflen) +{ + return send_le (slot, class, ins, p0, p1, + lc, data, le, + retbuf, retbuflen, + NULL); +} + + +/* Send an APDU to the card in SLOT. The APDU is created from all given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for LC won't sent this field and the data field; in this case DATA must also be passed as NULL. The return value is the status word or -1 @@ -2716,8 +2798,8 @@ int apdu_send (int slot, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) { - return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, - retbuf, retbuflen); + return send_le (slot, class, ins, p0, p1, lc, data, 256, + retbuf, retbuflen, NULL); } /* Send an APDU to the card in SLOT. The APDU is created from all @@ -2730,7 +2812,25 @@ int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int lc, const char *data) { - return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL); + return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL); +} + + +/* Same as apdu_send_simple but uses the keypad of the reader. */ +int +apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, + 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; + return send_le (slot, class, ins, p0, p1, lc, data, -1, + NULL, NULL, &pininfo); } @@ -2771,7 +2871,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, class = apdulen? *apdu : 0; resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_direct(%d) failed: %s\n", @@ -2825,7 +2925,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_direct(%d) for get response failed: %s\n", diff --git a/scd/apdu.h b/scd/apdu.h index 45388fdd1..c3af82506 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -63,7 +63,8 @@ enum { SW_HOST_CARD_IO_ERROR = 0x1000a, SW_HOST_GENERAL_ERROR = 0x1000b, SW_HOST_NO_READER = 0x1000c, - SW_HOST_ABORTED = 0x1000d + SW_HOST_ABORTED = 0x1000d, + SW_HOST_NO_KEYPAD = 0x1000e }; @@ -96,8 +97,14 @@ int apdu_activate (int slot); int apdu_reset (int slot); 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_send_simple (int slot, int class, int ins, int p0, int p1, int lc, const char *data); +int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, + int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen); int apdu_send (int slot, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 28b70c466..75cd12c59 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -1,5 +1,5 @@ /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application. - * Copyright (C) 2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -280,10 +280,11 @@ verify_pin (app_t app, { if (!app->did_chv1 || app->force_chv1 ) { + const char *s; char *pinvalue; int rc; - rc = pincb (pincb_arg, "PIN", &pinvalue); + rc = pincb (pincb_arg, "PIN", &pinvalue); if (rc) { log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); @@ -291,8 +292,16 @@ verify_pin (app_t app, } /* We require the PIN to be at least 6 and at max 8 bytes. - According to the specs, this should all be ASCII but we don't - check this. */ + According to the specs, this should all be ASCII. */ + for (s=pinvalue; digitp (s); s++) + ; + if (*s) + { + log_error ("Non-numeric digits found in PIN\n"); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + if (strlen (pinvalue) < 6) { log_error ("PIN is too short; minimum length is 6\n"); @@ -307,6 +316,28 @@ verify_pin (app_t app, } rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + if (gpg_err_code (rc) == GPG_ERR_INV_VALUE) + { + /* We assume that ISO 9564-1 encoding is used and we failed + because the first nibble we passed was 3 and not 2. DIN + says something about looking up such an encoding in the + SSD but I was not able to find any tag relevant to + this. */ + char paddedpin[8]; + int i, ndigits; + + for (ndigits=0, s=pinvalue; *s; ndigits++, s++) + ; + i = 0; + paddedpin[i++] = 0x20 | (ndigits & 0x0f); + for (s=pinvalue; i < sizeof paddedpin && *s && s[1]; s = s+2 ) + paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f)); + if (i < sizeof paddedpin && *s) + paddedpin[i++] = (((*s - '0') << 4) | 0x0f); + while (i < sizeof paddedpin) + paddedpin[i++] = 0xff; + rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin); + } if (rc) { log_error ("verify PIN failed\n"); @@ -404,7 +435,7 @@ app_select_dinsig (APP app) int slot = app->slot; int rc; - rc = iso7816_select_application (slot, aid, sizeof aid); + rc = iso7816_select_application (slot, aid, sizeof aid, 0); if (!rc) { app->apptype = "DINSIG"; diff --git a/scd/app-nks.c b/scd/app-nks.c index b6a3037ed..73ec8ea01 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -27,7 +27,7 @@ #include <time.h> #include "scdaemon.h" - +#include "i18n.h" #include "iso7816.h" #include "app-common.h" #include "tlv.h" @@ -320,7 +320,7 @@ verify_pin (app_t app, return rc; } - /* The follwoing limits are due to TCOS but also defined in the + /* The following limits are due to TCOS but also defined in the NKS specs. */ if (strlen (pinvalue) < 6) { @@ -340,7 +340,10 @@ verify_pin (app_t app, rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue)); if (rc) { - log_error ("verify PIN failed\n"); + if ( gpg_error (rc) == GPG_ERR_USE_CONDITIONS ) + log_error (_("the NullPIN has not yet been changed\n")); + else + log_error ("verify PIN failed\n"); xfree (pinvalue); return rc; } @@ -492,7 +495,7 @@ app_select_nks (APP app) int slot = app->slot; int rc; - rc = iso7816_select_application (slot, aid, sizeof aid); + rc = iso7816_select_application (slot, aid, sizeof aid, 0); if (!rc) { app->apptype = "NKS"; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 5625c729b..3d04be0be 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1284,6 +1284,11 @@ verify_chv2 (app_t app, if (!app->did_chv2) { char *pinvalue; + iso7816_pininfo_t pininfo; + + memset (&pininfo, 0, sizeof pininfo); + pininfo.mode = 1; + pininfo.minlen = 6; rc = pincb (pincb_arg, "PIN", &pinvalue); if (rc) @@ -2455,7 +2460,9 @@ app_select_openpgp (app_t app) size_t buflen; void *relptr; - rc = iso7816_select_application (slot, aid, sizeof aid); + /* Note that the card can't cope with P2=0xCO, thus we need to pass a + special flag value. */ + rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001); if (!rc) { unsigned int manufacturer; diff --git a/scd/app-p15.c b/scd/app-p15.c index 739a9ef95..8bb94cfcd 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -3268,18 +3268,15 @@ app_select_p15 (app_t app) int direct = 0; int is_belpic = 0; - rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid); - if (rc) - { - rc = iso7816_select_application (slot, pkcs15be_aid,sizeof pkcs15be_aid); - if (!rc) - is_belpic = 1; - } + rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0); if (rc) { /* Not found: Try to locate it from 2F00. We use direct path selection here because it seems that the Belgian eID card does only allow for that. Many other cards supports this - selection method too. */ + selection method too. Note, that we don't use + select_application above for the Belgian card - the call + works but it seems that it did not switch to the correct DF. + Using the 2f02 just works. */ unsigned short path[1] = { 0x2f00 }; rc = iso7816_select_path (app->slot, path, 1, NULL, NULL); diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index f82d93b00..fee733358 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1240,7 +1240,9 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, if (CCID_COMMAND_FAILED (buffer)) print_command_failed (buffer); - /* Check whether a card is at all available. */ + /* Check whether a card is at all available. Note: If you add new + error codes here, check whether they need to be ignored in + send_escape_cmd. */ switch ((buffer[7] & 0x03)) { case 0: /* no error */ break; @@ -1253,16 +1255,23 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, /* Note that this function won't return the error codes NO_CARD or - CARD_INACTIVE */ + CARD_INACTIVE. IF RESULT is not NULL, the result from the + operation will get returned in RESULT and its length in RESULTLEN. + If the response is larger than RESULTMAX, an error is returned and + the required buffer length returned in RESULTLEN. */ static int send_escape_cmd (ccid_driver_t handle, - const unsigned char *data, size_t datalen) + const unsigned char *data, size_t datalen, + unsigned char *result, size_t resultmax, size_t *resultlen) { int i, rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; + if (resultlen) + *resultlen = 0; + if (datalen > sizeof msg - 10) return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ @@ -1285,11 +1294,42 @@ send_escape_cmd (ccid_driver_t handle, return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, seqno, 5000, 0); - + if (result) + switch (rc) + { + /* We need to ignore certain errorcode here. */ + case 0: + case CCID_DRIVER_ERR_CARD_INACTIVE: + case CCID_DRIVER_ERR_NO_CARD: + { + if (msglen > resultmax) + rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */ + else + { + memcpy (result, msg, msglen); + *resultlen = msglen; + } + rc = 0; + } + break; + default: + break; + } + return rc; } +int +ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); +} + + + /* experimental */ int ccid_poll (ccid_driver_t handle) @@ -1445,7 +1485,8 @@ ccid_get_atr (ccid_driver_t handle, { tried_iso = 1; /* Try switching to ISO mode. */ - if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2)) + if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, + NULL, 0, NULL)) goto again; } else if (CCID_COMMAND_FAILED (msg)) @@ -1957,14 +1998,16 @@ ccid_transceive (ccid_driver_t handle, } -/* Send the CCID Secure command to the reader. APDU_BUF should contain the APDU template. PIN_MODE defines now the pin gets formatted: +/* Send the CCID Secure command to the reader. APDU_BUF should + contain the APDU template. PIN_MODE defines how the pin gets + formatted: 1 := The PIN is ASCII encoded and of variable length. The length of the PIN entered will be put into Lc by the reader. The APDU should me made up of 4 bytes without Lc. PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 - may be used t enable usbale defaults. PIN_PADLEN should be 0 + may be used t enable reasonable defaults. PIN_PADLEN should be 0. When called with RESP and NRESP set to NULL, the function will merely check whether the reader supports the secure command for the @@ -1996,7 +2039,7 @@ ccid_transceive_secure (ccid_driver_t handle, else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */ else - return CCID_DRIVER_ERR_NOT_SUPPORTED; + return CCID_DRIVER_ERR_NO_KEYPAD; if (pin_mode != 1) return CCID_DRIVER_ERR_NOT_SUPPORTED; @@ -2027,7 +2070,8 @@ ccid_transceive_secure (ccid_driver_t handle, if (handle->id_vendor == VENDOR_SCM) { DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); - rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3); + rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, + NULL, 0, NULL); if (rc) return rc; } @@ -2044,7 +2088,7 @@ ccid_transceive_secure (ccid_driver_t handle, if (handle->id_vendor == VENDOR_SCM) { /* For the SPR532 the next 2 bytes need to be zero. We do this - for all SCM product. Kudos to to Martin Paljak for this + for all SCM product. Kudos to Martin Paljak for this hint. */ msg[13] = msg[14] = 0; } diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h index 1b9ac2f85..6f6527108 100644 --- a/scd/ccid-driver.h +++ b/scd/ccid-driver.h @@ -58,7 +58,7 @@ #ifndef CCID_DRIVER_H #define CCID_DRIVER_H -/* The CID driver returns the same error codes as the statsu words +/* The CID driver returns the same error codes as the status words used by GnuPG's apdu.h. For ease of maintenance they should always match. */ #define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001 @@ -74,6 +74,7 @@ #define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b #define CCID_DRIVER_ERR_NO_READER 0x1000c #define CCID_DRIVER_ERR_ABORTED 0x1000d +#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e struct ccid_driver_s; typedef struct ccid_driver_s *ccid_driver_t; @@ -94,6 +95,10 @@ int ccid_transceive_secure (ccid_driver_t handle, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen, unsigned char *resp, size_t maxresplen, size_t *nresp); +int ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, + size_t *nresp); diff --git a/scd/iso7816.c b/scd/iso7816.c index 5b985324f..5c62e1371 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -47,9 +47,9 @@ #define CMD_SELECT_FILE 0xA4 -#define CMD_VERIFY 0x20 -#define CMD_CHANGE_REFERENCE_DATA 0x24 -#define CMD_RESET_RETRY_COUNTER 0x2C +#define CMD_VERIFY ISO7816_VERIFY +#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA +#define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER #define CMD_GET_DATA 0xCA #define CMD_PUT_DATA 0xDA #define CMD_MSE 0x22 @@ -95,6 +95,7 @@ map_sw (int sw) case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break; case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break; case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break; + case SW_HOST_NO_KEYPAD: ec = GPG_ERR_NOT_SUPPORTED; break; default: if ((sw & 0x010000)) @@ -124,12 +125,15 @@ iso7816_map_sw (int sw) requested application ID. The function can't be used to enumerate AIDs and won't return the AID on success. The return value is 0 for okay or a GPG error code. Note that ISO error codes are - internally mapped. */ + internally mapped. Bit 0 of FLAGS should be set if the card does + not understand P2=0xC0. */ gpg_error_t -iso7816_select_application (int slot, const char *aid, size_t aidlen) +iso7816_select_application (int slot, const char *aid, size_t aidlen, + unsigned int flags) { int sw; - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, + (flags&1)? 0 :0x0c, aidlen, aid); return map_sw (sw); } @@ -221,27 +225,59 @@ iso7816_list_directory (int slot, int list_dirs, } +/* Check whether the reader supports the ISO command code COMMAND on + the keypad. Returns 0 on success. */ +gpg_error_t +iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo) +{ + int sw; + + sw = apdu_check_keypad (slot, command, + pininfo->mode, pininfo->minlen, pininfo->maxlen, + pininfo->padlen); + return map_sw (sw); +} + /* Perform a VERIFY command on SLOT using the card holder verification - vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ + vector CHVNO with a CHV of lenght CHVLEN. With PININFO non-NULL + the keypad of the reader will be used. Returns 0 on success. */ gpg_error_t -iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) +iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen, + iso7816_pininfo_t *pininfo) { int sw; - sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); + 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, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); return map_sw (sw); } +/* Perform a VERIFY command on SLOT using the card holder verification + vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ +gpg_error_t +iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) +{ + return iso7816_verify_kp (slot, chvno, chv, chvlen, NULL); +} + /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN 0), a "change reference data" is done, otherwise an "exchange reference data". The new reference data is expected in NEWCHV of - length NEWCHVLEN. */ + length NEWCHVLEN. With PININFO non-NULL the keypad of the reader + will be used. */ gpg_error_t -iso7816_change_reference_data (int slot, int chvno, - const char *oldchv, size_t oldchvlen, - const char *newchv, size_t newchvlen) +iso7816_change_reference_data_kp (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen, + iso7816_pininfo_t *pininfo) { int sw; char *buf; @@ -258,28 +294,69 @@ iso7816_change_reference_data (int slot, int chvno, memcpy (buf, oldchv, oldchvlen); memcpy (buf+oldchvlen, newchv, newchvlen); - sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, - oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); + if (pininfo && pininfo->mode) + sw = apdu_send_simple_kp (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, + oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen); + else + sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, + oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); xfree (buf); return map_sw (sw); } +/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder + verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN + 0), a "change reference data" is done, otherwise an "exchange + reference data". The new reference data is expected in NEWCHV of + length NEWCHVLEN. */ +gpg_error_t +iso7816_change_reference_data (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen) +{ + return iso7816_change_reference_data_kp (slot, chvno, oldchv, oldchvlen, + newchv, newchvlen, NULL); +} + + gpg_error_t -iso7816_reset_retry_counter (int slot, int chvno, - const char *newchv, size_t newchvlen) +iso7816_reset_retry_counter_kp (int slot, int chvno, + const char *newchv, size_t newchvlen, + iso7816_pininfo_t *pininfo) { int sw; if (!newchv || !newchvlen ) return gpg_error (GPG_ERR_INV_VALUE); - sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, - 2, chvno, newchvlen, newchv); + if (pininfo && pininfo->mode) + sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER, + 2, chvno, newchvlen, newchv, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen); + else + sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, + 2, chvno, newchvlen, newchv); return map_sw (sw); } +gpg_error_t +iso7816_reset_retry_counter (int slot, int chvno, + const char *newchv, size_t newchvlen) +{ + return iso7816_reset_retry_counter_kp (slot, chvno, newchv, newchvlen, NULL); +} + + + /* Perform a GET DATA command requesting TAG and storing the result in a newly allocated buffer at the address passed by RESULT. Return the length of this data at the address of RESULTLEN. */ diff --git a/scd/iso7816.h b/scd/iso7816.h index 04c7ae63e..8f7907405 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -28,10 +28,30 @@ #include "cardglue.h" #endif +/* Command codes used by iso7816_check_keypad. */ +#define ISO7816_VERIFY 0x20 +#define ISO7816_CHANGE_REFERENCE_DATA 0x24 +#define ISO7816_RESET_RETRY_COUNTER 0x2C + + +/* Information to be passed to keypad equipped readers. See + ccid-driver.c for details. */ +struct iso7816_pininfo_s +{ + int mode; /* A mode of 0 means: Do not use the keypad. */ + int minlen; + int maxlen; + int padlen; + int padchar; +}; +typedef struct iso7816_pininfo_s iso7816_pininfo_t; + + gpg_error_t iso7816_map_sw (int sw); gpg_error_t iso7816_select_application (int slot, - const char *aid, size_t aidlen); + const char *aid, size_t aidlen, + unsigned int flags); gpg_error_t iso7816_select_file (int slot, int tag, int is_dir, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_select_path (int slot, @@ -39,13 +59,26 @@ gpg_error_t iso7816_select_path (int slot, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_list_directory (int slot, int list_dirs, unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_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_change_reference_data (int slot, int chvno, const char *oldchv, size_t oldchvlen, const char *newchv, size_t newchvlen); +gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen, + iso7816_pininfo_t *pininfo); gpg_error_t iso7816_reset_retry_counter (int slot, int chvno, const char *newchv, size_t newchvlen); +gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno, + const char *newchv, + size_t newchvlen, + iso7816_pininfo_t *pininfo); gpg_error_t iso7816_get_data (int slot, int tag, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_put_data (int slot, int tag, diff --git a/scd/scdaemon.c b/scd/scdaemon.c index c6995abcc..56c0d7600 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -84,6 +84,7 @@ enum cmd_and_opt_values opcscDriver, oDisableCCID, oDisableOpenSC, + oDisableKeypad, oAllowAdmin, oDenyAdmin, oDisableApplication, @@ -126,6 +127,7 @@ static ARGPARSE_OPTS opts[] = { "@" #endif /* end --disable-ccid */}, + { oDisableKeypad, "disable-keypad", 0, N_("do not use a reader's keypad")}, { oAllowAdmin, "allow-admin", 0, N_("allow the use of admin card commands")}, { oDenyAdmin, "deny-admin", 0, "@" }, { oDisableApplication, "disable-application", 2, "@"}, @@ -135,7 +137,7 @@ static ARGPARSE_OPTS opts[] = { /* The card dirver we use by default for PC/SC. */ -#ifdef HAVE_W32_SYSTEM +#if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__) #define DEFAULT_PCSC_DRIVER "winscard.dll" #else #define DEFAULT_PCSC_DRIVER "libpcsclite.so" @@ -489,6 +491,8 @@ main (int argc, char **argv ) case oDisableCCID: opt.disable_ccid = 1; break; case oDisableOpenSC: break; + case oDisableKeypad: opt.disable_keypad = 1; break; + case oAllowAdmin: opt.allow_admin = 1; break; case oDenyAdmin: opt.allow_admin = 0; break; diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 54566b6ad..abe9730a7 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -39,21 +39,22 @@ #define MAX_DIGEST_LEN 24 -/* A large struct name "opt" to keep global flags */ +/* A large struct name "opt" to keep global flags. */ struct { - unsigned int debug; /* debug flags (DBG_foo_VALUE) */ - int verbose; /* verbosity level */ - int quiet; /* be as quiet as possible */ - int dry_run; /* don't change any persistent data */ - int batch; /* batch mode */ - const char *homedir; /* configuration directory name */ + unsigned int debug; /* Debug flags (DBG_foo_VALUE). */ + int verbose; /* Verbosity level. */ + int quiet; /* Be as quiet as possible. */ + int dry_run; /* Don't change any persistent data. */ + int batch; /* Batch mode. */ + const char *homedir; /* Configuration directory name. */ const char *ctapi_driver; /* Library to access the ctAPI. */ const char *pcsc_driver; /* Library to access the PC/SC system. */ const char *reader_port; /* NULL or reder port to use. */ int disable_ccid; /* Disable the use of the internal CCID driver. */ + int disable_keypad; /* Do not use a keypad. */ int allow_admin; /* Allow the use of admin commands for certain cards. */ - strlist_t disabled_applications; /* card applications we do not + strlist_t disabled_applications; /* Card applications we do not want to use. */ } opt; |