Friday, December 19, 2014

OS X Yosemite bug: SCardStatus returns SCARD_E_INSUFFICIENT_BUFFER

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

SCardStatus returns SCARD_E_INSUFFICIENT_BUFFER

SCardStatus() does not correctly report the reader name length. The terminating NUL character of the reader name is not counted. So a second call to SCardStatus() using the size returned by the first SCardStatus() call will fail with 0x80100008 (that is SCARD_E_INSUFFICIENT_BUFFER).

It is a really stupid bug and very easy to fix for Apple.

See also

"Problem with SCardStatus on MAC OS X 10.10 (Yosemite)" at https://smartcardservices.macosforge.org/trac/ticket/140.

Apple bug report #19306215 "PC/SC SCardStatus returns SCARD_E_INSUFFICIENT_BUFFER". Closed by Apple, 9th January 2015, as a duplicated of #18890042.

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;
 }

 /* 1st SCardStatus call to get the reader name length */
 char name[100];
 DWORD len;
 err = SCardStatus(hCard, NULL, &len, NULL, NULL, NULL, NULL);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardStatus: 0x%08x\n",err);
  return -1;
 }
 printf("reader name length: %d\n", len);
 //len += 1;

 /* 2nd SCardStatus call to get the reader name value */
 err = SCardStatus(hCard, name, &len, NULL, NULL, NULL, NULL);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardStatus: 0x%08x\n",err);
  return -1;
 }
 printf("Reader name: %s (%ld)\n", name, strlen(name));

 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
reader name length: 22
SCardStatus: 0x80100008

If I uncomment the line: len += 1; I get:
$ ./main
Reader: Gemalto PC Twin Reader
reader name length: 22
Reader name: Gemalto PC Twin Reader (22)

Note that the reader length returned by the first SCardStatus() call is identical to the value returned by strlen(name). And strlen() does NOT includes the terminating NUL character.

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
reader length: 29
Reader name: Gemalto PC Twin Reader 00 00 (28)

Note that the length returned by the first SCardStatus() call is strlen(name) + 1.

Known workaround

Add 1 to the reader length returned by SCardStatus().

Update

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