Tuesday, December 16, 2014

OS X Yosemite bug: SCardStatus() after a card reset

This is part of the series: "OS X Yosemite and smart cards: known bugs".

SCardStatus() after a card reset

SCardStatus() does not corectly detect card reset any more on OS X 10.10 Yosemite.

If a card has been reseted using another PC/SC context then SCardStatus() will not fail with 0x80100068 (for SCARD_W_RESET_CARD) as before but will succeed instead.
The problem will be detected later by, for example, the next SCardTransmit() returning SCARD_W_RESET_CARD.

The problem is similar to the one described in OS X Yosemite bug: SCardBeginTransaction() after a card reset.

What is strange is that SCardTransmit() erroneously returns SCARD_W_RESET_CARD after a SCardReconnect(..., SCARD_RESET_CARD, ...) (see OS X Yosemite bug: SCardReconnect) but SCardStatus() do not return SCARD_W_RESET_CARD in the same case. The card reset (or not) state is managed in a "funny" way inside PC/SC on Yosemite.

See also

Apple bug report #19264087 "PC/SC function SCardStatus() fails to fail after a card reset"
#19264087 closed as a duplicate of #18689292.

Sample code

#include <stdio.h>
#include <stdlib.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;
 }

 /* create a second PC/SC handle and reset the card */
 SCARDHANDLE hCard2;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard2, &dwActiveProtocol);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardConnect: 0x%08x\n",err);
  return -1;
 }
 err = SCardDisconnect(hCard2, SCARD_RESET_CARD);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardDisconnect: 0x%08x\n",err);
 }

 /* SCardStatus should fail with SCARD_W_RESET_CARD */
 char name[100];
 DWORD len = sizeof name;
 DWORD dwState, dwProtocol;
 unsigned char atr[MAX_ATR_SIZE];
 DWORD atr_len = sizeof atr;
 err = SCardStatus(hCard, name, &len, &dwState, &dwProtocol, atr, &atr_len);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardStatus: 0x%08x\n",err);
  return -1;
 }
 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 SCardReleaseContext(hContext);

 return 0;
}

Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC    main.c   -o main

An error 0x80100068 (for SCARD_W_RESET_CARD) is expected here.
$ ./main 
Reader: Gemalto PC Twin Reader

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
Reader: Gemalto PC Twin Reader 00 00
SCardStatus: 0x80100068

Known workaround

None known.

Modify your code to check for  SCARD_W_RESET_CARD returned by SCardTransmit() even after a successful SCardStatus().

Update

This bug is now fixed in Mac OS X Yosemite 10.10.2.