Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Tuesday, December 15, 2015

OS X El Capitan bug: SCardBeginTransaction() returns different error codes than on GNU/Linux and Windows

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

SCardBeginTransaction() returns different error codes than on GNU/Linux and Windows

I found 2 cases in which SCardBeginTransaction() on El Capitan returns error codes that are different than on GNU/Linux and Windows.

SCardBeginTransaction() returns SCARD_W_RESET_CARD after a card change

On El Capitan SCardBeginTransaction() returns SCARD_W_RESET_CARD if the card has been removed and inserted again in the reader.

On GNU/Linux and Windows SCardBeginTransaction() returns SCARD_W_REMOVED_CARD instead.

SCardBeginTransaction() returns SCARD_W_UNPOWERED_CARD after a card is unpowered

The same function SCardBeginTransaction() returns SCARD_W_UNPOWERED_CARD on Mac OS X El Capitan if the card has been powered off by another application.

On GNU/Linux and Windows SCardBeginTransaction() returns SCARD_W_RESET_CARD instead.

See also

Apple bug report #23900844 "PC/SC SCardBeginTransaction() returns different error codes than on GNU/Linux and Windows"

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

#define pcsc_error(fct) printf(fct ": %s 0x%08lX\n", pcsc_stringify_error(err), (long)err)

int main(void)
{
 LPSTR mszReaders;
 DWORD err, cchReaders = 0;
 SCARDCONTEXT hContext = 0;
 SCARDHANDLE hCard = 0;

 err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 if (err != SCARD_S_SUCCESS) {
  pcsc_error("ScardEstablishedContext");
  return -1;
 }

 err = SCardListReaders(hContext, NULL, NULL, &cchReaders);
 if (err != 0) {
  pcsc_error("ScardListReaders");
  return -1;
 }
 mszReaders = calloc(cchReaders, sizeof(char));
 if (!mszReaders) {
  printf("calloc\n");
  return -1;
 }
 err = SCardListReaders(hContext,NULL, mszReaders, &cchReaders);
 if (err != SCARD_S_SUCCESS) {
  pcsc_error("ScardListReaders");
  return -1;
 }

 printf("Using reader: %s\n", mszReaders);

 DWORD dwActiveProtocol;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("SCardConnect");
        return 0;
    }

 printf("Remove and insert the card. Then press enter\n");
 getchar();

 err = SCardBeginTransaction(hCard);
 if (err != SCARD_S_SUCCESS)
  pcsc_error("SCardBeginTransaction");

 err = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 if (err != SCARD_S_SUCCESS)
  pcsc_error("SCardDisconnect");

 SCardReleaseContext(hContext);

 return 0;
}

Reset program

Using the Python PC/SC wrapper PySCard to have a shorter code.

#! /usr/bin/env python

from smartcard.System import readers
from smartcard.scard import SCARD_UNPOWER_CARD, SCARD_RESET_CARD

reader = readers()[0]
print "Using:", reader

connection = reader.createConnection()
#connection.connect(disposition=SCARD_RESET_CARD)
connection.connect(disposition=SCARD_UNPOWER_CARD)

If you modify the reset program to use disposition=SCARD_RESET_CARD then SCardBeginTransaction() will return SCARD_W_RESET_CARD as is also the case on GNU/Linux and Windows.

Result (on El Capitan)

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

$ ./main
Using reader: Gemalto PC Twin Reader
Remove and insert the card. Then press enter

SCardBeginTransaction: Card was reset. 0x80100068

In case of unpower (instead of card removal) by a second application:
$ ./main
Using reader: Gemalto PC Twin Reader
Remove and insert the card. Then press enter

SCardBeginTransaction: Card is unpowered. 0x80100067

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
Using reader: Gemalto PC Twin Reader 00 00
Remove and insert the card. Then press enter

SCardBeginTransaction: Card was removed. 0x80100069

In case of unpower:
$ ./main
Using reader: Gemalto PC Twin Reader 00 00
Remove and insert the card. Then press enter

SCardBeginTransaction: Card was reset. 0x80100068

Known workaround

None known.

I have not checked the same code on Mavericks (OS X 10.9) to see if it is a regression or not.
I guess the behaviour changed in Yosemite with the rewrite of PC/SC (see OS X Yosemite and smart cards status)