OS X Yosemite bug: SCardReconnect
This is part of the series: "OS X Yosemite and smart cards: known bugs".
SCardReconnect
SCardReconnect() has a bad side effect on Yosemite.After a
SCardReconnect(..., SCARD_RESET_CARD, ...)
the next SCardTransmit() will fail with the error code 0x80100068
that is SCARD_W_RESET_CARD
.This should not happen since
SCardReconnect()
should reconnect (sic) to the card after the card has been reseted.See also
"[OSX 10.10] After connect and SCardReconnect, SCardTransmit raises card reset" https://smartcardservices.macosforge.org/trac/ticket/136Apple bug report #19262854 "PC/SC function SCardReconnect does not work as expected". Closed by Apple, 6th January 2015, as a duplicated of #18689292.
Sample code
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef __APPLE__ #include <PCSC/winscard.h> #include <PCSC/wintypes.h> #else #include <winscard.h> #endif int main(int argc, const char * argv[]) { SCARDCONTEXT hContext; LPSTR mszReaders; DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (err != SCARD_S_SUCCESS) { printf("ScardEstablishedContext: 0x%08x\n",err); return -1; } DWORD cchReaders = 0; err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders); if (err != 0) { printf("ScardListReaders: 0x%08x\n",err); return -1; } mszReaders = calloc(cchReaders, sizeof(char)); if (!mszReaders) { printf("calloc\n"); return -1; } err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders); if (err != SCARD_S_SUCCESS) { printf("ScardListReaders: 0x%08x\n",err); return -1; } printf("Reader: %s\n", mszReaders); SCARDHANDLE hCard; DWORD dwActiveProtocol; err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); if (err != SCARD_S_SUCCESS) { printf("ScardConnect: 0x%08x\n",err); return -1; } unsigned char cmd[] = {0, 0, 0, 0}; unsigned char resp[255]; DWORD resp_len = sizeof resp; SCARD_IO_REQUEST * pci; err = SCardReconnect(hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &dwActiveProtocol); if (err != SCARD_S_SUCCESS) { printf("SCardReconnect: 0x%08x\n",err); } if (SCARD_PROTOCOL_T0 == dwActiveProtocol) pci = SCARD_PCI_T0; else pci = SCARD_PCI_T1; err = SCardTransmit(hCard, pci, cmd, sizeof cmd, pci, resp, &resp_len); if (err != SCARD_S_SUCCESS) { printf("SCardTransmit: 0x%08x\n",err); } SCardDisconnect(hCard, SCARD_LEAVE_CARD); SCardReleaseContext(hContext); return 0; }
Result (on Yosemite)
$ CFLAGS="-framework PCSC" make main cc -framework PCSC main.c -o main
$ ./main Reader: Gemalto PC Twin Reader SCardTransmit: 0x80100068
Expected result (on Debian)
$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main cc -pthread -I/usr/include/PCSC -lpcsclite main.c -o main main.c: In function ‘main’: main.c:57:7: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default] main.c:59:7: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
No error expected:
$ ./main Reader: Gemalto PC Twin Reader 00 00
Known workaround
You can add an second call toSCardReconnect(..., SCARD_LEAVE_CARD, ...)
after the first one to force a card reconnection.Or you can replace the bogus SCardReconnect() by the sequence:
SCardDisconnect(..., SCARD_RESET_CARD); SCardConnect(...);
I could not find an elegant way to write a macro to automate the code change. The problem is that
SCardConnect()
needs parameters (PC/SC context and reader name) that are not available from the SCardReconnect()
call.