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.