OS X El Capitan bug: SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED instead of SCARD_E_INSUFFICIENT_BUFFER

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

SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED instead of SCARD_E_INSUFFICIENT_BUFFER

SCardGetAttrib() does not work correctly on El Capitan 10.11.4 (on Yosemite the function was not usable at all. See "OS X Yosemite bug: SCardGetAttrib").

When the buffer is too small to store the result the function may return SCARD_E_NOT_TRANSACTED instead of the expected SCARD_E_INSUFFICIENT_BUFFER.

The problem is not present for all the attributes. For example it is the case for SCARD_ATTR_ATR_STRING (Answer to reset (ATR) string) but not for SCARD_ATTR_VENDOR_IFD_SERIAL_NO (Vendor-supplied interface device serial number). This is because for SCARD_ATTR_ATR_STRING the CCID diver checks the buffer size and returns IFD_ERROR_INSUFFICIENT_BUFFER to the PC/SC middleware. In the case of SCARD_ATTR_VENDOR_IFD_SERIAL_NO the buffer size is not checked by the CCID driver and the CCID returns IFD_SUCCESS (the buffer between pcscd and the driver is 264 bytes long and is enough to store a serial number).

It should be the job of the PC/SC middleware to check the user provided buffer is large enough and return SCARD_E_INSUFFICIENT_BUFFER to the application when needed.

See also

Apple bug report #25463286 "PCSC SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED instead of SCARD_E_INSUFFICIENT_BUFFER"

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(int argc, const char * argv[])
{
    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 {
        DWORD attrLen = 33;
        unsigned char attr[attrLen];
        unsigned int i;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        } else {
            printf("SCARD_ATTR_ATR_STRING: %d\n", attrLen);
            for (i=0; i<attrLen; i++)
                printf("%02X ", attr[i]);
            printf("\n");
        }

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

The sample code performs 2 calls to SCardGetAttrib() with 2 different buffer sizes.
  • The first time the size is 33 bytes and is big enough to contain the ATR.
  • The second time the size is 2 bytes and is obviously too short.

Result (on El Capitan 10.11.4)

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

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

As expected the first SCardGetAttrib() call succeeds and returns the card ATR value.

The second call with pcbAttrLen set to 2 fails and returns the unexpected error SCARD_E_NOT_TRANSACTED.

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 (70D7E2EE) 00 00
attrLen: 23
SCARD_ATTR_ATR_STRING: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 
attrLen: 2
SCardGetAttrib: Insufficient buffer. 0x80100008

On GNU/Linux we have the expected behavior: SCARD_E_INSUFFICIENT_BUFFER is returned on the second call.

Known workaround

None known.
Be sure to always use big enough buffers.

Update: 27th September 2016

This bug is fixed in macOS Sierra 10.12.0.