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);
- On the first call the
pbAttr
pointer is NULL because no buffer is used. This call will just but the correct buffer size in thepcbAttrLen
parameter (namedattrLen
here). - A big enough buffer is allocated to store the attribute value.
- 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 forattrLen
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.