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, April 19, 2016

OS X El Capitan bug: SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED when it should not

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

SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED when it should not

SCardGetAttrib() does not work correctly any more on El Capitan. SCardGetAttrib() is used to get an attribute value from the IFD Handler (the smart card reader driver).

One idea of SCardGetAttrib() is to use a double call:
  • 1st call to get the correct buffer size to store the attribute
  • 2nd call to fill the allocated buffer with the requested attribute value

The program then looks like:
rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &attrLen);
  attr = malloc(attrLen);
  rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);

  1. On the first call the pbAttr pointer is NULL because no buffer is used. This call will just but the correct buffer size in the pcbAttrLen parameter (named attrLen here).
  2. A big enough buffer is allocated to store the attribute value.
  3. On the second call the buffer is passed as argument and is filled by the SCardGetAttrib() call.

I discovered that the first call to SCardGetAttrib() fails and returns SCARD_E_NOT_TRANSACTED if the value in attrLen (given to the function) is lower than the needed buffer size.

This input value (buffer size) should just be ignored by PC/SC since the buffer pointer is NULL (no buffer is used). The parameter is used to get a value from the function, not to give a value to the function.

See also

Apple bug report #25802143 "PCSC SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED when it should not"

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 SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag)))
#define SCARD_CLASS_ICC_STATE       9   /**< ICC State specific definitions */
#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) /**< Answer to reset (ATR) string. */

#define RED "\33[01;31m"
#define NORMAL "\33[0m"
#define pcsc_error(fct) printf(RED fct ": %s 0x%08X\n" NORMAL, pcsc_stringify_error(err), err)

int main(void)
{
    SCARDCONTEXT hContext;
    LPSTR mszReaders;
    DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardEstablishedContext");
        return -1;
    }
    DWORD cchReaders = 0;
    err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
    if (err != 0) {
        pcsc_error("ScardListReaders");
        return -1;
    }
    mszReaders = calloc(cchReaders, sizeof(char));
    if (!mszReaders) {
        pcsc_error("calloc\n");
        return -1;
    }
    err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardListReaders");
        return -1;
    }

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

    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_RAW, &hCard, &dwActiveProtocol);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardConnect");
    } else {
        unsigned char attr[33] = { 0 };
        unsigned int i;
        DWORD attrLen;

        attrLen = 22;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        }

        attrLen = 123456;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        }

        attrLen = sizeof attr;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        } else {
            for (i=0; i<attrLen; i++)
                printf("%02X ", attr[i]);
            printf("\n");
        }
    }
    SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    SCardReleaseContext(hContext);
    return 0;
}


Result (on El Capitan)

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

Reader: Gemalto PC Twin Reader
attrLen: 22
SCardGetAttrib: Transaction failed. 0x80100016
attrLen: 23
attrLen: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 

Here the requested attribute is the card ATR (dwAttrId = SCARD_ATTR_ATR_STRING) with a size of 23 bytes. If the first call to SCardGetAttrib() uses a value of 22 (or less) then the function returns with the error SCARD_E_NOT_TRANSACTED (0x80100016)

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

Reader: Gemalto PC Twin Reader (70D7E2EE) 00 00
attrLen: 23
attrLen: 23
attrLen: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 

Known workaround

Do not use a random value for attrLen on the first call (or you will get random results). But initialize the variable to a big enough value. A value of 65535 should be good enough in most cases.

Update: 27th September 2016

This bugs is fixed in macOS Sierra 10.12.0.