macOS Sierra bug: SCardTransmit() silently truncates the card response
This is the first new PC/SC bug I find in macOS Sierra.
I imagine this bug is also present in El Capitan and Yosemite but I have not checked.
SCardTransmit() returns SCARD_S_SUCCESS when it should return SCARD_E_INSUFFICIENT_BUFFER
SCardTransmit() is used to transfer a command to the smart card and get the smart card answer.If the reception buffer is not large enough to contain the card answer the PC/SC error
SCARD_E_INSUFFICIENT_BUFFER
should be returned and the expected size indicated in the pcbRecvLength
parameter.Instead, on macOS Sierra,
SCARD_S_SUCCESS
is returned and the card response is truncated with no indication that something went wrong.See also
Apple bug report #30868184 "PC/SC SCardTransmit() silently truncates the smart card response".Sample code
#include <stdio.h> #include <string.h> #ifdef __APPLE__ #include <PCSC/winscard.h> #include <PCSC/wintypes.h> #else #include <winscard.h> #endif #define CHECK_RV(fct) if (SCARD_S_SUCCESS != rv) { printf(fct"() failed: %s\n", pcsc_stringify_error(rv)); ret = 0; goto error; } else { printf(fct"(): OK\n"); } int main(void) { int ret = 1; SCARDCONTEXT hContext; SCARDHANDLE hCard; DWORD dwActiveProtocol; LONG rv; char mszReaders[1024]; DWORD dwReaders = sizeof(mszReaders); SCARD_IO_REQUEST ioRecvPci = *SCARD_PCI_T0; /* use a default value */ unsigned char bSendBuffer[MAX_BUFFER_SIZE]; unsigned char bRecvBuffer[MAX_BUFFER_SIZE]; DWORD send_length, length; rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); CHECK_RV("SCardEstablishContext"); rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders); CHECK_RV("SCardListReaders"); rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); CHECK_RV("SCardConnect"); send_length = 5; /* GET RANDOM for a GPK card */ memcpy(bSendBuffer, "\x80\x84\x00\x00\x20", send_length); /* expected size is 0x20 + 2 = 34 bytes */ length = 2; printf("Given length: %d\n", length); rv = SCardTransmit(hCard, SCARD_PCI_T0, bSendBuffer, send_length, &ioRecvPci, bRecvBuffer, &length); printf("Expected length: %d\n", length); if (SCARD_E_INSUFFICIENT_BUFFER == rv) printf("test PASS. Insufficient buffer is expected\n"); else printf("test FAIL\n"); CHECK_RV("SCardTransmit"); if (SCARD_S_SUCCESS == rv) { size_t i; for (i=0; i<length; i++) printf("%02X ", bRecvBuffer[i]); printf("\n"); } rv = SCardDisconnect(hCard, SCARD_UNPOWER_CARD); CHECK_RV("SCardDisconnect") rv = SCardReleaseContext(hContext); CHECK_RV("SCardReleaseContext") error: return ret; }
The program sends a GET RANDOM command to a GPK card (very old card from Gemplus). The card will answer with 32 random bytes + 2 bytes for SW.
You can use any command that sends at least one bye of data instead.
Result (on Sierra)
$ cc -framework PCSC -g -Wall main.c -o main
SCardEstablishContext(): OK SCardListReaders(): OK SCardConnect(): OK Given length: 2 Expected length: 2 test FAIL SCardTransmit(): OK 5B 8F SCardDisconnect(): OK SCardReleaseContext(): OK
The value "5B 8F" is just the 2 first bytes returned by the card. The other 30 bytes and the status word (SW) are lost.
I note that if I use the values 0 or 1 for length then
SCardTransmit()
correctly returns SCARD_E_INSUFFICIENT_BUFFER
. So the Sierra code has a check to reject a buffer of smaller than 2 bytes. The code should check the given size compared to the real card answer.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
SCardEstablishContext(): OK SCardListReaders(): OK SCardConnect(): OK Given length: 2 Expected length: 34 test PASS. Insufficient buffer is expected SCardTransmit() failed: Insufficient buffer.
On Debian we get the expected
SCARD_E_INSUFFICIENT_BUFFER
error. And we also get the correct length value to store the complete card answer. Here 34 bytes.Known workaround
Be sure your reception buffer is large enough to contain the card answer + 2 bytes for SW.This should be the case for all correctly written application. That explains why nobody found the bug earlier.
I found the problem while playing with a particular PC/SC Unitary Test (for pcsc-lite) on macOS: BufferOverflow_SCardTransmit.c