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...

Monday, February 9, 2015

OS X Yosemite bug: SCardTransmit returns SCARD_W_UNPOWERED_CARD

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

SCardTransmit

SCardTransmit() do not work reliably any more on Yosemite. In some cases using 2 PC/SC applications the functions returns SCARD_W_UNPOWERED_CARD (or 0x0x80100067).

Interpretation

My interpretation is that Apple tried to implement a card auto power off mechanism. I added this feature in pcsc-lite version 1.6.5 from 2010. See "Card auto power on and off".

The card auto power off feature is visible with some smart card readers, like the Gemalto PC Twin (renamed PC USB TR and then renamed IDBridge CT30).


The reader has a LED in it. If the LED blinks then the reader is powered.  If the LED is ON then the smart card is powered.

On Mavericks (and the previous versions of Mac OS X) when you insert a card the LED is ON (unless the reader driver is not found) and stays ON until you remove the card.
On Yosemite when you insert a smart card the LED is ON and after 5 seconds the LED blinks.  This indicates that the card is powered for 5 seconds and then the card is powered off. Great.

Bug

The problem is that the Apple code is bogus. When 2 PC/SC applications are using the smart smart card at the same time the 5 seconds delay before powering off the card is started when the first application releases the card, even if the second application still has an access to the smart card.
After the 5 seconds delay the second application will get a SCARD_W_UNPOWERED_CARD error on the next SCardTransmit() call.

It looks like Apple forgot to power off the card only if NO other PC/SC application is using the card, not just after the first SCardDisconnect().

The sleep(5) in the sample code is important. This is the delay needed for the PC/SC layer to decide to power off the card. You can also remove this delay and wait before you press enter to continue the first execution.

See also

Apple bug report #19764910 "PC/SC: SCardTransmit returns SCARD_W_UNPOWERED_CARD"

The bug has been closed by Apple on 8th April 2015 as fixed in 10.10.3.

Sample code

#include <stdio.h>
#include <stdlib.h>
#include <unistd.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 : %08x\n",err);
  return -1;
    }

 DWORD cchReaders = 0;
 err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
 if (err != 0) {
  printf("ScardListReaders : %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 : %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 : %08x\n",err);
  return -1;
 }

 unsigned char cmd[] = {0, 0, 0, 0, 0};
 unsigned char resp[255];
 DWORD resp_len;
 SCARD_IO_REQUEST *pci;

 if (SCARD_PROTOCOL_T0 == dwActiveProtocol)
  pci = SCARD_PCI_T0;
 else
  pci = SCARD_PCI_T1;

 resp_len = sizeof resp;
 err = SCardTransmit(hCard, pci, cmd, sizeof cmd, NULL, resp, &resp_len);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardTransmit: %08x\n",err);
  return -1;
 }
 printf("SCardTransmit: %0X\n", err);

 if (1 == argc)
 {
  printf("Run the second instance\n");
  getchar();
 }

 resp_len = sizeof resp;
 err = SCardTransmit(hCard, pci, cmd, sizeof cmd, NULL, resp, &resp_len);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardTransmit: %08x\n",err);
  return -1;
 }
 printf("SCardTransmit: %0X\n", err);

 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 SCardReleaseContext(hContext);

 if (argc > 1)
 {
  /* The sleeping time is important here */
  sleep(5);
  printf("Continue the first instance\n");
 }

    return 0;
}

Result (on Yosemite)

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

Since this sample code needs two programs you need to have two Terminal windows opened.
In the first terminal you run ./main and in the second terminal you run ./main a (with an argument).
Once the second execution has finished you press enter in the first terminal to continue the execution of the first program.

1st terminal2nd terminal
$ ./main
Reader : Gemalto PC Twin Reader
SCardTransmit: 0
Run the second instance
$ ./main a
Reader : Gemalto PC Twin Reader
SCardTransmit: 0
SCardTransmit: 0
Continue the first instance
ScardTransmit: 80100067

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

1st terminal2nd terminal
$ ./main
Reader : Gemalto PC Twin Reader
SCardTransmit: 0
Run the second instance
$ ./main a
Reader : Gemalto PC Twin Reader
SCardTransmit: 0
SCardTransmit: 0
Continue the first instance
ScardTransmit: 0

Known workaround

None known.

One very bad (and untested) solution would be to create an application that sends an APDU (with no side effect) every 3 or 4 seconds so that the 5 seconds delay never occurs. But that is a very very bad solution. And it may be difficult or impossible to find an APDU that would be safe to send at any time.

Update

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