/* rdesktop: A Remote Desktop Protocol client. Smart Card support Copyright (C) Alexi Volkov 2006 Copyright 2010-2013 Pierre Ossman for Cendio AB Copyright 2011-2017 Henrik Andersson for Cendio AB Copyright 2015 Rostislav Kondratenko Copyright 2017 Karl Mikaelsson for Cendio AB Copyright 2016-2018 Alexander Zakharov This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #define SCARD_CTL_CODE(code) (0x42000000 + (code)) #else #include #include #include #ifdef PCSCLITE_VERSION_NUMBER #include #endif #endif /* PCSC_OSX */ #include "rdesktop.h" #include "scard.h" /* variable segment */ #define SCARD_MAX_MEM 102400 #ifndef SCARD_AUTOALLOCATE #define SCARD_AUTOALLOCATE -1 #endif #define OUT_STREAM_SIZE 4096 #ifdef B_ENDIAN #define swap32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | \ (((x) & 0xff0000) >> 8) | (((x) & 0xff000000) >> 24)) #define swap16(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) #else #define swap32(x) (x) #define swap16(x) (x) #endif static pthread_mutex_t **scard_mutex = NULL; static uint32 curEpoch = 0, curDevice = 0, curId = 0, curBytesOut = 0; static PSCNameMapRec nameMapList = NULL; static int nameMapCount = 0; static pthread_t queueHandler; static pthread_mutex_t queueAccess; static pthread_cond_t queueEmpty; static pthread_mutex_t hcardAccess; static PMEM_HANDLE threadListHandle = NULL; static PThreadListElement threadList = NULL; static PSCThreadData queueFirst = NULL, queueLast = NULL; static int threadCount = 0; static PSCHCardRec hcardFirst = NULL; static void *queue_handler_function(void *data); /* code segment */ void scardSetInfo(uint32 epoch, uint32 device, uint32 id, uint32 bytes_out) { curDevice = device; curId = id; curBytesOut = bytes_out; curEpoch = epoch; } static RD_NTSTATUS scard_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition, uint32 flags_and_attributes, char *filename, RD_NTHANDLE * phandle) { UNUSED(device_id); UNUSED(accessmask); UNUSED(sharemode); UNUSED(create_disposition); UNUSED(flags_and_attributes); UNUSED(filename); UNUSED(phandle); return RD_STATUS_SUCCESS; } static RD_NTSTATUS scard_close(RD_NTHANDLE handle) { UNUSED(handle); return RD_STATUS_SUCCESS; } static RD_NTSTATUS scard_read(RD_NTHANDLE handle, uint8 * data, uint32 length, uint64 offset, uint32 * result) { UNUSED(handle); UNUSED(data); UNUSED(length); UNUSED(offset); UNUSED(result); return RD_STATUS_SUCCESS; } static RD_NTSTATUS scard_write(RD_NTHANDLE handle, uint8 * data, uint32 length, uint64 offset, uint32 * result) { UNUSED(handle); UNUSED(data); UNUSED(length); UNUSED(offset); UNUSED(result); return RD_STATUS_SUCCESS; } /* Enumeration of devices from rdesktop.c */ /* returns numer of units found and initialized. */ /* optarg looks like ':"ReaderName=ReaderAlias"' */ /* when it arrives to this function. */ int scard_enum_devices(uint32 * id, char *optarg) { char *name = optarg + 1; char *alias; int count = 0; PSCNameMapRec tmpMap; MYPCSC_DWORD rv; SCARDCONTEXT hContext; /* code segment */ rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Error, "scard_enum_devices(), PCSC service not available"); return 0; } else rv = SCardReleaseContext(hContext); count = 0; if (0 != pthread_mutex_init(&queueAccess, NULL)) { logger(SmartCard, Error, "scard_enum_devices(), can't initialize queue access mutex"); return 0; } if (0 != pthread_cond_init(&queueEmpty, NULL)) { logger(SmartCard, Error, "scard_enum_devices(), can't initialize queue control cv"); return 0; } if (0 != pthread_mutex_init(&hcardAccess, NULL)) { logger(SmartCard, Error, "scard_enum_devices(), can't initialize hcard list access mutex"); return 0; } if (0 != pthread_create(&queueHandler, NULL, (void *(*)(void *)) queue_handler_function, NULL)) { logger(SmartCard, Error, "scard_enum_devices(), can't create queue handling thread"); return 0; } strncpy(g_rdpdr_device[*id].name, "SCARD\0\0\0", 8); toupper_str(g_rdpdr_device[*id].name); g_rdpdr_device[*id].local_path = "/dev/scard"; g_rdpdr_device[*id].pdevice_data = NULL; g_rdpdr_device[*id].handle = 0; g_rdpdr_device[*id].device_type = DEVICE_TYPE_SCARD; count++; (*id)++; if (*optarg == ':') { while ((optarg = next_arg(name, ',')) && *id < RDPDR_MAX_DEVICES) { int len; char *vendor = NULL; alias = next_arg(name, '='); vendor = next_arg(alias, ';'); if (strlen(name) > 0) { if (!strlen(alias)) { alias = name; vendor = "\0"; } logger(SmartCard, Debug, "scard_enum_devices(), name='%s', alias='%s', vendor='%s'", name, alias, vendor); nameMapCount++; if (nameMapList == NULL) nameMapList = xmalloc(nameMapCount * sizeof(TSCNameMapRec)); else nameMapList = xrealloc(nameMapList, nameMapCount * sizeof(TSCNameMapRec)); tmpMap = nameMapList + nameMapCount - 1; STRNCPY(tmpMap->alias, alias, sizeof(tmpMap->alias)); STRNCPY(tmpMap->name, name, sizeof(tmpMap->name)); if (vendor) { len = strlen(vendor); if (len > 0) { memset(tmpMap->vendor, 0, 128); STRNCPY(tmpMap->vendor, vendor, sizeof(tmpMap->vendor)); } else tmpMap->vendor[0] = '\0'; } else tmpMap->vendor[0] = '\0'; } name = optarg; } } return count; } typedef struct _scard_handle_list_t { struct _scard_handle_list_t *next; /* PCSC handle is datatype long which is arch size-dependent */ long handle; /* RDP server handles are always 32 bit */ uint32_t server; } _scard_handle_list_t; static uint32_t g_scard_handle_counter = 0; static _scard_handle_list_t *g_scard_handle_list = NULL; static void _scard_handle_list_add(long handle); static void _scard_handle_list_remove(long handle); static uint32_t _scard_handle_list_get_server_handle(long handle); static long _scard_handle_list_get_pcsc_handle(uint32_t server); void _scard_handle_list_add(long handle) { _scard_handle_list_t *list = g_scard_handle_list; /* we don't care of order of list so to simplify the add we add new items to front of list */ _scard_handle_list_t *item = xmalloc(sizeof(_scard_handle_list_t)); item->next = list; item->handle = handle; /* lookup first unused handle id */ int overlap = 0; if (g_scard_handle_counter == 0) g_scard_handle_counter++; while (_scard_handle_list_get_pcsc_handle(g_scard_handle_counter)) { g_scard_handle_counter++; if (g_scard_handle_counter == 0 && overlap) { logger(SmartCard, Error, "_scard_handle_list_add(), broken smartcard client software, handles are not freed and no more handles left to allocate"); abort(); } if (g_scard_handle_counter == 0) overlap = g_scard_handle_counter = 1; } item->server = g_scard_handle_counter; g_scard_handle_list = item; } void _scard_handle_list_remove(long handle) { _scard_handle_list_t *item, *list, *prev_item; prev_item = NULL; item = list = g_scard_handle_list; while (item) { if (item->handle == handle) { /* unlink from list */ if (prev_item) prev_item->next = item->next; else g_scard_handle_list = item->next; xfree(item); break; } /* store previous item for relinking */ prev_item = item; item = item->next; } } uint32_t _scard_handle_list_get_server_handle(long handle) { _scard_handle_list_t *item; item = g_scard_handle_list; while (item) { if (item->handle == handle) return item->server; item = item->next; } return 0; } long _scard_handle_list_get_pcsc_handle(uint32_t server) { _scard_handle_list_t *item; item = g_scard_handle_list; while (item) { if (item->server == server) return item->handle; item = item->next; } return 0; } static void * SC_xmalloc(PMEM_HANDLE * memHandle, unsigned int size) { PMEM_HANDLE handle = NULL; if (size > 0 && memHandle) { handle = xmalloc(size + sizeof(MEM_HANDLE)); if (handle) { handle->prevHandle = NULL; handle->nextHandle = NULL; handle->dataSize = size; if (*memHandle) { handle->prevHandle = *memHandle; (*memHandle)->nextHandle = handle; } *memHandle = handle; return handle + 1; } else return NULL; } else return NULL; } static void SC_xfree(PMEM_HANDLE * handle, void *memptr) { if (memptr != NULL) { PMEM_HANDLE lcHandle = (PMEM_HANDLE) memptr - 1; if (lcHandle->dataSize > 0) { memset(memptr, 0, lcHandle->dataSize); if (lcHandle->nextHandle) lcHandle->nextHandle->prevHandle = lcHandle->prevHandle; if (lcHandle->prevHandle) lcHandle->prevHandle->nextHandle = lcHandle->nextHandle; if (*handle == lcHandle) { if (lcHandle->prevHandle) *handle = lcHandle->prevHandle; else *handle = lcHandle->nextHandle; } xfree(lcHandle); } } } static void SC_xfreeallmemory(PMEM_HANDLE * handle) { if (handle && (*handle)) { if ((*handle)->prevHandle) { (*handle)->prevHandle->nextHandle = NULL; SC_xfreeallmemory(&((*handle)->prevHandle)); } if ((*handle)->nextHandle) { (*handle)->nextHandle->prevHandle = NULL; SC_xfreeallmemory(&((*handle)->nextHandle)); } memset(*handle, 0, (*handle)->dataSize + sizeof(MEM_HANDLE)); xfree(*handle); *handle = NULL; } } /* ---------------------------------- */ static char * getName(char *alias) { int i; PSCNameMapRec tmpMap; for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++) { if (strcmp(tmpMap->alias, alias) == 0) return tmpMap->name; } return alias; } static char * getVendor(char *name) { int i; PSCNameMapRec tmpMap; for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++) { if (strcmp(tmpMap->name, name) == 0) return tmpMap->vendor; } return NULL; } static char * getAlias(char *name) { int i; PSCNameMapRec tmpMap; for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++) { if (strcmp(tmpMap->name, name) == 0) return tmpMap->alias; } return name; } static int hasAlias(char *name) { int i; PSCNameMapRec tmpMap; for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++) { if (strcmp(tmpMap->name, name) == 0) return 1; } return 0; } static void inRepos(STREAM in, unsigned int read) { SERVER_DWORD add = 4 - read % 4; if (add < 4 && add > 0) { in_uint8s(in, add); } } static void outRepos(STREAM out, unsigned int written) { SERVER_DWORD add = (4 - written % 4) % 4; if (add > 0) { out_uint8s(out, add); } } static void outBufferStartWithLimit(STREAM out, int length, int highLimit) { int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length)); out_uint32_le(out, header); out_uint32_le(out, 0x00000001); /* Magic DWORD - any non zero */ } static void outBufferStart(STREAM out, int length) { outBufferStartWithLimit(out, length, 0x7FFFFFFF); } static void outBufferFinishWithLimit(STREAM out, char *buffer, unsigned int length, unsigned int highLimit) { unsigned int header = (length > highLimit) ? (highLimit) : (length); out_uint32_le(out, header); if (length <= 0) { out_uint32_le(out, 0x00000000); } else { if (header < length) length = header; out_uint8a(out, buffer, length); outRepos(out, length); } } static void outBufferFinish(STREAM out, char *buffer, unsigned int length) { outBufferFinishWithLimit(out, buffer, length, 0x7FFFFFFF); } static void outForceAlignment(STREAM out, unsigned int seed) { SERVER_DWORD add = (seed - s_tell(out) % seed) % seed; if (add > 0) out_uint8s(out, add); } static unsigned int inString(PMEM_HANDLE * handle, STREAM in, char **destination, SERVER_DWORD dataLength, RD_BOOL wide) { unsigned int Result = (wide) ? (2 * dataLength) : (dataLength); PMEM_HANDLE lcHandle = NULL; char *buffer = SC_xmalloc(&lcHandle, Result + 2); char *reader; /* code segment */ if (wide) { unsigned int i; in_uint8a(in, buffer, 2 * dataLength); for (i = 0; i < dataLength; i++) if ((buffer[2 * i] < 0) || (buffer[2 * i + 1] != 0)) buffer[i] = '?'; else buffer[i] = buffer[2 * i]; } else { in_uint8a(in, buffer, dataLength); } buffer[dataLength] = '\0'; reader = getName(buffer); *destination = SC_xmalloc(handle, strlen(reader) + 1); strcpy(*destination, reader); SC_xfreeallmemory(&lcHandle); return Result; } static unsigned int outString(STREAM out, char *source, RD_BOOL wide) { PMEM_HANDLE lcHandle = NULL; char *reader = getAlias(source); unsigned int dataLength = strlen(reader) + 1; unsigned int Result = (wide) ? (2 * dataLength) : (dataLength); /* code segment */ if (wide) { unsigned int i; char *buffer = SC_xmalloc(&lcHandle, Result); for (i = 0; i < dataLength; i++) { if (source[i] < 0) buffer[2 * i] = '?'; else buffer[2 * i] = reader[i]; buffer[2 * i + 1] = '\0'; } out_uint8a(out, buffer, 2 * dataLength); } else { out_uint8a(out, reader, dataLength); } SC_xfreeallmemory(&lcHandle); return Result; } static void inReaderName(PMEM_HANDLE * handle, STREAM in, char **destination, RD_BOOL wide) { SERVER_DWORD dataLength; in_uint8s(in, 0x08); in_uint32_le(in, dataLength); inRepos(in, inString(handle, in, destination, dataLength, wide)); } static void inSkipLinked(STREAM in) { SERVER_DWORD len; in_uint32_le(in, len); if (len > 0) { in_uint8s(in, len); inRepos(in, len); } } /* ---------------------------------- */ /* Smart Card processing functions: */ /* ---------------------------------- */ static MYPCSC_DWORD SC_returnCode(MYPCSC_DWORD rc, PMEM_HANDLE * handle, STREAM in, STREAM out) { UNUSED(in); SC_xfreeallmemory(handle); out_uint8s(out, 256); return rc; } static MYPCSC_DWORD SC_returnNoMemoryError(PMEM_HANDLE * handle, STREAM in, STREAM out) { return SC_returnCode(SCARD_E_NO_MEMORY, handle, in, out); } static MYPCSC_DWORD TS_SCardEstablishContext(STREAM in, STREAM out) { UNUSED(in); MYPCSC_DWORD rv; MYPCSC_SCARDCONTEXT myHContext; SERVER_SCARDCONTEXT hContext; hContext = 0; /* code segment */ logger(SmartCard, Debug, "TS_SCardEstablishContext()"); rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &myHContext); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardEstablishContext(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); goto bail_out; } if (!myHContext) { logger(SmartCard, Debug, "TS_SCardEstablishedContext(), myHContext == NULL"); goto bail_out; } /* add context to list of handle and get server handle */ _scard_handle_list_add(myHContext); hContext = _scard_handle_list_get_server_handle(myHContext); logger(SmartCard, Debug, "TS_SCardEstablishContext(), success. context: 0x%08x, [0x%lx]", hContext, myHContext); bail_out: out_uint32_le(out, 0x00000004); out_uint32_le(out, hContext); /* must not be 0 (Seems to be pointer), don't know what is this (I use hContext as value) */ /* I hope it's not a pointer because i just downcasted it - jlj */ out_uint32_le(out, 0x00000004); out_uint32_le(out, hContext); outForceAlignment(out, 8); s_mark_end(out); return rv; } static MYPCSC_DWORD TS_SCardReleaseContext(STREAM in, STREAM out) { MYPCSC_DWORD rv; MYPCSC_SCARDCONTEXT myHContext; SERVER_SCARDCONTEXT hContext; in_uint8s(in, 0x1C); in_uint32_le(in, hContext); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardReleaseContext(), context=0x%08x [0x%lx]", (unsigned) hContext, myHContext); rv = SCardReleaseContext(myHContext); _scard_handle_list_remove(myHContext); if (rv) { logger(SmartCard, Debug, "TS_SCardReleaseContext(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardReleaseContext(), success, context: 0x%08x, [0x%lx]", hContext, myHContext); } outForceAlignment(out, 8); s_mark_end(out); return rv; } static MYPCSC_DWORD TS_SCardIsValidContext(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; in_uint8s(in, 0x1C); in_uint32_le(in, hContext); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardIsValidContext(), context: 0x%08x [0x%lx]", (unsigned) hContext, myHContext); rv = SCardIsValidContext(myHContext); if (rv) { logger(SmartCard, Debug, "TS_SCardIsValidContext(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); rv = SCARD_E_INVALID_HANDLE; } else { logger(SmartCard, Debug, "TS_SCardIsValidContext(), success, context: 0x%08x, [0x%lx]", hContext, myHContext); } outForceAlignment(out, 8); s_mark_end(out); return rv; } static MYPCSC_DWORD TS_SCardListReaders(STREAM in, STREAM out, RD_BOOL wide) { #define readerArraySize 1024 MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; SERVER_DWORD dataLength; MYPCSC_DWORD cchReaders = readerArraySize; size_t plen1, plen2, pend; char *readers, *cur; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x2C); in_uint32_le(in, hContext); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardListReaders(), context: 0x%08x [0x%lx])", (unsigned) hContext, myHContext); plen1 = s_tell(out); out_uint32_le(out, 0x00000000); /* Temp value for data length as 0x0 */ out_uint32_le(out, 0x01760650); plen2 = s_tell(out); out_uint32_le(out, 0x00000000); /* Temp value for data length as 0x0 */ dataLength = 0; readers = SC_xmalloc(&lcHandle, readerArraySize); if (!readers) return SC_returnNoMemoryError(&lcHandle, in, out); readers[0] = '\0'; readers[1] = '\0'; rv = SCardListReaders(myHContext, NULL, readers, &cchReaders); cur = readers; if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardListReaders(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { int i; PSCNameMapRec tmpMap; logger(SmartCard, Debug, "TS_SCardListReaders(), success, context: 0x%08x [0x%lx]", (unsigned) hContext, myHContext); for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++) { dataLength += outString(out, tmpMap->alias, wide); } int lenSC = strlen(cur); if (lenSC == 0) dataLength += outString(out, "\0", wide); else while (lenSC > 0) { if (!hasAlias(cur)) { logger(SmartCard, Debug, "TS_SCardListReaders(), '%s'", cur); dataLength += outString(out, cur, wide); } cur = (void *) ((unsigned char *) cur + lenSC + 1); lenSC = strlen(cur); } } dataLength += outString(out, "\0", wide); outRepos(out, dataLength); s_mark_end(out); pend = s_tell(out); s_seek(out, plen1); out_uint32_le(out, dataLength); s_seek(out, plen2); out_uint32_le(out, dataLength); s_seek(out, pend); outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static MYPCSC_DWORD TS_SCardConnect(STREAM in, STREAM out, RD_BOOL wide) { MYPCSC_DWORD rv; SCARDCONTEXT myHContext; SERVER_SCARDCONTEXT hContext; char *szReader; SERVER_DWORD dwShareMode; SERVER_DWORD dwPreferredProtocol; MYPCSC_SCARDHANDLE myHCard; SERVER_SCARDHANDLE hCard; MYPCSC_DWORD dwActiveProtocol; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x1C); in_uint32_le(in, dwShareMode); in_uint32_le(in, dwPreferredProtocol); inReaderName(&lcHandle, in, &szReader, wide); in_uint8s(in, 0x04); in_uint32_le(in, hContext); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardConnect(), context: 0x%08x [0x%lx], share: 0x%08x, proto: 0x%08x, reader: '%s'", (unsigned) hContext, myHContext, (unsigned) dwShareMode, (unsigned) dwPreferredProtocol, szReader ? szReader : "NULL"); rv = SCardConnect(myHContext, szReader, (MYPCSC_DWORD) dwShareMode, (MYPCSC_DWORD) dwPreferredProtocol, &myHCard, &dwActiveProtocol); hCard = 0; if (myHCard) { _scard_handle_list_add(myHCard); hCard = _scard_handle_list_get_server_handle(myHCard); } switch (rv) { case SCARD_S_SUCCESS: { char *szVendor = getVendor(szReader); logger(SmartCard, Debug, "TS_SCardConnect(), success, hcard: 0x%08x [0x%lx]", (unsigned) hCard, myHCard); if (szVendor && (strlen(szVendor) > 0)) { logger(SmartCard, Debug, "TS_SCardConnect(), set attribute ATTR_VENDOR_NAME"); pthread_mutex_lock(&hcardAccess); PSCHCardRec hcard = xmalloc(sizeof(TSCHCardRec)); if (hcard) { hcard->hCard = hCard; hcard->vendor = szVendor; hcard->next = NULL; hcard->prev = NULL; if (hcardFirst) { hcardFirst->prev = hcard; hcard->next = hcardFirst; } hcardFirst = hcard; } pthread_mutex_unlock(&hcardAccess); } } break; default: logger(SmartCard, Debug, "TS_SCardConnect(), SCardConnect failed: %s (0x%08x)", pcsc_stringify_error(rv), rv); break; } out_uint32_le(out, 0x00000000); out_uint32_le(out, 0x00000000); out_uint32_le(out, 0x00000004); out_uint32_le(out, 0x016Cff34); /* if the active protocol > 4 billion, this is trouble. odds are low */ out_uint32_le(out, (SERVER_DWORD) dwActiveProtocol); out_uint32_le(out, 0x00000004); out_uint32_le(out, hCard); outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static MYPCSC_DWORD TS_SCardReconnect(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; SERVER_SCARDHANDLE hCard; MYPCSC_SCARDHANDLE myHCard; SERVER_DWORD dwShareMode; SERVER_DWORD dwPreferredProtocol; SERVER_DWORD dwInitialization; MYPCSC_DWORD dwActiveProtocol; in_uint8s(in, 0x20); in_uint32_le(in, dwShareMode); in_uint32_le(in, dwPreferredProtocol); in_uint32_le(in, dwInitialization); in_uint8s(in, 0x04); in_uint32_le(in, hContext); in_uint8s(in, 0x04); in_uint32_le(in, hCard); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardReconnect(), context: 0x%08x, hcard: 0x%08x [%lx], share: 0x%08x, proto: 0x%08x, init: 0x%08x", (unsigned) hContext, (unsigned) hCard, myHCard, (unsigned) dwShareMode, (unsigned) dwPreferredProtocol, (unsigned) dwInitialization); rv = SCardReconnect(myHCard, (MYPCSC_DWORD) dwShareMode, (MYPCSC_DWORD) dwPreferredProtocol, (MYPCSC_DWORD) dwInitialization, &dwActiveProtocol); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardReconnect(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardReconnect(), success, proto=0x%08x", (unsigned) dwActiveProtocol); } out_uint32_le(out, (SERVER_DWORD) dwActiveProtocol); outForceAlignment(out, 8); s_mark_end(out); return rv; } static MYPCSC_DWORD TS_SCardDisconnect(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; SERVER_SCARDHANDLE hCard; MYPCSC_SCARDHANDLE myHCard; SERVER_DWORD dwDisposition; in_uint8s(in, 0x20); in_uint32_le(in, dwDisposition); in_uint8s(in, 0x04); in_uint32_le(in, hContext); in_uint8s(in, 0x04); in_uint32_le(in, hCard); myHContext = _scard_handle_list_get_pcsc_handle(hContext); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardDisconnect(), context: 0x%08x [0x%lx], hcard: 0x%08x [0x%lx], disposition: 0x%08x", (unsigned) hContext, myHContext, (unsigned) hCard, myHCard, (unsigned) dwDisposition); pthread_mutex_lock(&hcardAccess); PSCHCardRec hcard = hcardFirst; while (hcard) { if (hcard->hCard == hCard) { if (hcard->prev) hcard->prev->next = hcard->next; if (hcard->next) hcard->next->prev = hcard->prev; if (hcardFirst == hcard) hcardFirst = hcard->next; xfree(hcard); break; } hcard = hcard->next; } pthread_mutex_unlock(&hcardAccess); rv = SCardDisconnect(myHCard, (MYPCSC_DWORD) dwDisposition); _scard_handle_list_remove(myHCard); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardDisconnect(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardReconnect(), success"); } outForceAlignment(out, 8); s_mark_end(out); return rv; } /* Currently unused */ #if 0 static int needStatusRecheck(MYPCSC_DWORD rv, MYPCSC_LPSCARD_READERSTATE_A rsArray, SERVER_DWORD dwCount) { int i, recall = 0; if (rv == SCARD_S_SUCCESS) { MYPCSC_LPSCARD_READERSTATE_A cur; for (i = 0, cur = rsArray; i < dwCount; i++, cur++) { if (cur->dwEventState & SCARD_STATE_UNKNOWN) { cur->dwCurrentState = cur->dwEventState; recall++; } } } return recall; } static RD_BOOL mappedStatus(MYPCSC_DWORD code) { code >>= 16; code &= 0x0000FFFF; return (code % 2); } #endif static void copyReaderState_MyPCSCToServer(MYPCSC_LPSCARD_READERSTATE_A src, SERVER_LPSCARD_READERSTATE_A dst, MYPCSC_DWORD readerCount) { MYPCSC_LPSCARD_READERSTATE_A srcIter; SERVER_LPSCARD_READERSTATE_A dstIter; MYPCSC_DWORD i; for (i = 0, srcIter = src, dstIter = dst; i < readerCount; i++, srcIter++, dstIter++) { dstIter->szReader = srcIter->szReader; dstIter->pvUserData = srcIter->pvUserData; dstIter->dwCurrentState = srcIter->dwCurrentState; dstIter->dwEventState = srcIter->dwEventState; dstIter->cbAtr = srcIter->cbAtr; memcpy(dstIter->rgbAtr, srcIter->rgbAtr, MAX_ATR_SIZE * sizeof(unsigned char)); } } static void copyReaderState_ServerToMyPCSC(SERVER_LPSCARD_READERSTATE_A src, MYPCSC_LPSCARD_READERSTATE_A dst, SERVER_DWORD readerCount) { SERVER_LPSCARD_READERSTATE_A srcIter; MYPCSC_LPSCARD_READERSTATE_A dstIter; SERVER_DWORD i; for (i = 0, srcIter = src, dstIter = dst; i < readerCount; i++, srcIter++, dstIter++) { dstIter->szReader = srcIter->szReader; dstIter->pvUserData = srcIter->pvUserData; dstIter->dwCurrentState = srcIter->dwCurrentState; dstIter->dwEventState = srcIter->dwEventState; dstIter->cbAtr = srcIter->cbAtr; memcpy(dstIter->rgbAtr, srcIter->rgbAtr, MAX_ATR_SIZE * sizeof(unsigned char)); } } static MYPCSC_DWORD TS_SCardGetStatusChange(STREAM in, STREAM out, RD_BOOL wide) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; SERVER_DWORD dwTimeout; SERVER_DWORD dwCount; SERVER_DWORD dwPointerId; SERVER_LPSCARD_READERSTATE_A rsArray, cur; MYPCSC_LPSCARD_READERSTATE_A myRsArray; long i; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x18); in_uint32_le(in, dwTimeout); in_uint32_le(in, dwCount); in_uint8s(in, 0x08); in_uint32_le(in, hContext); in_uint8s(in, 0x04); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardGetStatusChange(), context: 0x%08x [0x%lx], timeout: 0x%08x, count: %d", (unsigned) hContext, myHContext, (unsigned) dwTimeout, (int) dwCount); if (dwCount > 0) { rsArray = SC_xmalloc(&lcHandle, dwCount * sizeof(SERVER_SCARD_READERSTATE_A)); if (!rsArray) return SC_returnNoMemoryError(&lcHandle, in, out); memset(rsArray, 0, dwCount * sizeof(SERVER_SCARD_READERSTATE_A)); for (i = 0, cur = rsArray; i < dwCount; i++, cur++) { in_uint32_le(in, dwPointerId); cur->szReader = (char *) (intptr_t) dwPointerId; in_uint32_le(in, cur->dwCurrentState); in_uint32_le(in, cur->dwEventState); in_uint32_le(in, cur->cbAtr); in_uint8a(in, cur->rgbAtr, sizeof(cur->rgbAtr)); } for (i = 0, cur = rsArray; i < dwCount; i++, cur++) { if (cur->szReader != NULL) { SERVER_DWORD dataLength; in_uint8s(in, 0x08); in_uint32_le(in, dataLength); inRepos(in, inString(&lcHandle, in, (char **) &(cur->szReader), dataLength, wide)); #if !WITH_PNP_NOTIFICATIONS if (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0) cur->dwCurrentState |= SCARD_STATE_IGNORE; #endif } logger(SmartCard, Debug, "TS_SCardGetStatusChange(), reader='%s', user=%p, state=0x%08x, event=0x%08x", cur->szReader ? cur->szReader : "NULL", cur->pvUserData, (unsigned) cur->dwCurrentState, (unsigned) cur->dwEventState); } } else { rsArray = NULL; } myRsArray = SC_xmalloc(&lcHandle, dwCount * sizeof(MYPCSC_SCARD_READERSTATE_A)); if (!myRsArray) return SC_returnNoMemoryError(&lcHandle, in, out); memset(myRsArray, 0, dwCount * sizeof(SERVER_SCARD_READERSTATE_A)); copyReaderState_ServerToMyPCSC(rsArray, myRsArray, (SERVER_DWORD) dwCount); /* Workaround for a bug in pcsc-lite, timeout value of 0 is handled as INFINIT but is by Windows PCSC spec. used for polling current state. */ if (dwTimeout == 0) dwTimeout = 1; rv = SCardGetStatusChange(myHContext, (MYPCSC_DWORD) dwTimeout, myRsArray, (MYPCSC_DWORD) dwCount); copyReaderState_MyPCSCToServer(myRsArray, rsArray, (MYPCSC_DWORD) dwCount); logger(SmartCard, Debug, "TS_SCardGetStatusChange(), SCardGetStatusChange returned \"%s\" (0x%08x)", pcsc_stringify_error(rv), rv); out_uint32_le(out, dwCount); out_uint32_le(out, 0x00084dd8); out_uint32_le(out, dwCount); for (i = 0, cur = rsArray; i < dwCount; i++, cur++) { logger(SmartCard, Debug, "TS_SCardGetStatusChange(), reader='%s', user=%p, state=0x%08x, event=0x%08x", cur->szReader ? cur->szReader : "NULL", cur->pvUserData, (unsigned) cur->dwCurrentState, (unsigned) cur->dwEventState); /* Do endian swaps... */ cur->dwCurrentState = swap32(cur->dwCurrentState); cur->dwEventState = swap32(cur->dwEventState); cur->cbAtr = swap32(cur->cbAtr); out_uint8a(out, (void *) ((unsigned char **) cur + 2), sizeof(SERVER_SCARD_READERSTATE_A) - 2 * sizeof(unsigned char *)); } outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static MYPCSC_DWORD TS_SCardCancel(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; in_uint8s(in, 0x1C); in_uint32_le(in, hContext); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardCancel(), context: 0x%08x [0x%08lx]", (unsigned) hContext, (unsigned long) myHContext); rv = SCardCancel(myHContext); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardCancel(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardCancel(), success"); } outForceAlignment(out, 8); s_mark_end(out); return rv; } static MYPCSC_DWORD TS_SCardLocateCardsByATR(STREAM in, STREAM out, RD_BOOL wide) { unsigned int i, j, k; MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; /* The SCARD_ATRMASK_L struct doesn't contain any longs or DWORDs - no need to split into SERVER_ and MYPCSC_ */ LPSCARD_ATRMASK_L pAtrMasks, cur; SERVER_DWORD atrMaskCount = 0; SERVER_DWORD readerCount = 0; SERVER_LPSCARD_READERSTATE_A rsArray, ResArray, rsCur; MYPCSC_LPSCARD_READERSTATE_A myRsArray; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x2C); in_uint32_le(in, hContext); in_uint32_le(in, atrMaskCount); pAtrMasks = SC_xmalloc(&lcHandle, atrMaskCount * sizeof(SCARD_ATRMASK_L)); if (!pAtrMasks) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, pAtrMasks, atrMaskCount * sizeof(SCARD_ATRMASK_L)); in_uint32_le(in, readerCount); rsArray = SC_xmalloc(&lcHandle, readerCount * sizeof(SCARD_READERSTATE)); if (!rsArray) return SC_returnNoMemoryError(&lcHandle, in, out); memset(rsArray, 0, readerCount * sizeof(SCARD_READERSTATE)); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardLocateCardsByATR(), context: 0x%08x [0x%08lx], atrs: %d, readers: %d", (unsigned) hContext, (unsigned long) myHContext, (int) atrMaskCount, (int) readerCount); for (i = 0, cur = pAtrMasks; i < atrMaskCount; i++, cur++) { cur->cbAtr = swap32(cur->cbAtr); /* Fixme, we might want to log the ATR and mask here */ } for (i = 0, rsCur = (SERVER_LPSCARD_READERSTATE_A) ((unsigned char **) rsArray + 2); i < readerCount; i++, rsCur++) { in_uint8s(in, 4); in_uint8a(in, rsCur, SERVER_SCARDSTATESIZE); } ResArray = SC_xmalloc(&lcHandle, readerCount * sizeof(SERVER_SCARD_READERSTATE_A)); if (!ResArray) return SC_returnNoMemoryError(&lcHandle, in, out); for (i = 0, rsCur = rsArray; i < readerCount; i++, rsCur++) { /* Do endian swaps... */ rsCur->dwCurrentState = swap32(rsCur->dwCurrentState); rsCur->dwEventState = swap32(rsCur->dwEventState); rsCur->cbAtr = swap32(rsCur->cbAtr); inReaderName(&lcHandle, in, (char **) &rsCur->szReader, wide); logger(SmartCard, Debug, "TS_SCardLocateCardsByATR(), reader='%s', user=%p, state=0x%08x, event=0x%08x", rsCur->szReader ? rsCur->szReader : "NULL", rsCur->pvUserData, (unsigned) rsCur->dwCurrentState, (unsigned) rsCur->dwEventState); } memcpy(ResArray, rsArray, readerCount * sizeof(SERVER_SCARD_READERSTATE_A)); /* FIXME segfault here. */ myRsArray = SC_xmalloc(&lcHandle, readerCount * sizeof(MYPCSC_SCARD_READERSTATE_A)); if (!myRsArray) return SC_returnNoMemoryError(&lcHandle, in, out); copyReaderState_ServerToMyPCSC(rsArray, myRsArray, readerCount); rv = SCardGetStatusChange(myHContext, 0x00000001, myRsArray, readerCount); copyReaderState_MyPCSCToServer(myRsArray, rsArray, readerCount); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardLocateCardsByATR(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardLocateCardsByATR(), success"); cur = pAtrMasks; for (i = 0, cur = pAtrMasks; i < atrMaskCount; i++, cur++) { for (j = 0, rsCur = rsArray; j < readerCount; j++, rsCur++) { RD_BOOL equal = 1; for (k = 0; k < cur->cbAtr; k++) { if ((cur->rgbAtr[k] & cur->rgbMask[k]) != (rsCur->rgbAtr[k] & cur->rgbMask[k])) { equal = 0; break; } } if (equal) { rsCur->dwEventState |= 0x00000040; /* SCARD_STATE_ATRMATCH 0x00000040 */ memcpy(ResArray + j, rsCur, sizeof(SCARD_READERSTATE)); logger(SmartCard, Debug, "TS_SCardLocateCardsByATR(), reader='%s', user=%p, state=0x%08x, event=0x%08x", rsCur->szReader ? rsCur->szReader : "NULL", rsCur->pvUserData, (unsigned) rsCur->dwCurrentState, (unsigned) rsCur->dwEventState); } } } } out_uint32_le(out, readerCount); out_uint32_le(out, 0x00084dd8); out_uint32_le(out, readerCount); for (i = 0, rsCur = ResArray; i < readerCount; i++, rsCur++) { /* Do endian swaps... */ rsCur->dwCurrentState = swap32(rsCur->dwCurrentState); rsCur->dwEventState = swap32(rsCur->dwEventState); rsCur->cbAtr = swap32(rsCur->cbAtr); out_uint8a(out, (void *) ((unsigned char **) rsCur + 2), sizeof(SCARD_READERSTATE) - 2 * sizeof(unsigned char *)); } outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static DWORD TS_SCardBeginTransaction(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; in_uint8s(in, 0x30); in_uint32_le(in, hCard); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardBeginTransaction(), hcard: 0x%08x [0x%lx])", (unsigned) hCard, myHCard); rv = SCardBeginTransaction(myHCard); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardBeginTransaction(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardBeginTransaction(), success"); } outForceAlignment(out, 8); s_mark_end(out); return rv; } static DWORD TS_SCardEndTransaction(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; SERVER_DWORD dwDisposition = 0; in_uint8s(in, 0x20); in_uint32_le(in, dwDisposition); in_uint8s(in, 0x0C); in_uint32_le(in, hCard); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardEndTransaction(), hcard: 0x%08x [0x%lx], disposition: 0x%08x)", (unsigned) hCard, (unsigned long) myHCard, (unsigned) dwDisposition); rv = SCardEndTransaction(myHCard, (MYPCSC_DWORD) dwDisposition); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardEndTransaction(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardEndTransaction(), success"); } outForceAlignment(out, 8); s_mark_end(out); return rv; } static void copyIORequest_MyPCSCToServer(MYPCSC_LPSCARD_IO_REQUEST src, SERVER_LPSCARD_IO_REQUEST dst) { unsigned char *srcBytes, *dstBytes; size_t bytesToCopy = src->cbPciLength - sizeof(MYPCSC_SCARD_IO_REQUEST); srcBytes = ((unsigned char *) src + sizeof(MYPCSC_SCARD_IO_REQUEST)); dstBytes = ((unsigned char *) dst + sizeof(SERVER_SCARD_IO_REQUEST)); dst->dwProtocol = swap32((uint32_t) src->dwProtocol); dst->cbPciLength = swap32((uint32_t) src->cbPciLength - sizeof(MYPCSC_SCARD_IO_REQUEST) + sizeof(SERVER_SCARD_IO_REQUEST)); memcpy(dstBytes, srcBytes, bytesToCopy); } static void copyIORequest_ServerToMyPCSC(SERVER_LPSCARD_IO_REQUEST src, MYPCSC_LPSCARD_IO_REQUEST dst) { unsigned char *srcBytes, *dstBytes; size_t bytesToCopy = src->cbPciLength - sizeof(SERVER_SCARD_IO_REQUEST); srcBytes = ((unsigned char *) src + sizeof(SERVER_SCARD_IO_REQUEST)); dstBytes = ((unsigned char *) dst + sizeof(MYPCSC_SCARD_IO_REQUEST)); dst->dwProtocol = swap32(src->dwProtocol); dst->cbPciLength = src->cbPciLength /* already correct endian */ - sizeof(SERVER_SCARD_IO_REQUEST) + sizeof(MYPCSC_SCARD_IO_REQUEST); memcpy(dstBytes, srcBytes, bytesToCopy); } static DWORD TS_SCardTransmit(STREAM in, STREAM out, uint32 srv_buf_len) { MYPCSC_DWORD rv; SERVER_DWORD map[7], linkedLen; void *tmp; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; SERVER_LPSCARD_IO_REQUEST pioSendPci, pioRecvPci; MYPCSC_LPSCARD_IO_REQUEST myPioSendPci, myPioRecvPci; unsigned char *sendBuf = NULL, *recvBuf = NULL; SERVER_DWORD cbSendLength, cbRecvLength; MYPCSC_DWORD myCbRecvLength; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x14); in_uint32_le(in, map[0]); in_uint8s(in, 0x04); in_uint32_le(in, map[1]); pioSendPci = SC_xmalloc(&lcHandle, sizeof(SERVER_SCARD_IO_REQUEST)); if (!pioSendPci) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, pioSendPci, sizeof(SERVER_SCARD_IO_REQUEST)); in_uint32_le(in, map[2]); in_uint32_le(in, cbSendLength); in_uint32_le(in, map[3]); in_uint32_le(in, map[4]); in_uint32_le(in, map[5]); in_uint32_le(in, cbRecvLength); if (map[0] & INPUT_LINKED) inSkipLinked(in); if (srv_buf_len <= cbRecvLength) cbRecvLength = srv_buf_len; in_uint8s(in, 0x04); in_uint32_le(in, hCard); myHCard = _scard_handle_list_get_pcsc_handle(hCard); if (map[2] & INPUT_LINKED) { in_uint32_le(in, linkedLen); pioSendPci->cbPciLength = linkedLen + sizeof(SERVER_SCARD_IO_REQUEST); tmp = SC_xmalloc(&lcHandle, pioSendPci->cbPciLength); if (!tmp) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, (void *) ((unsigned char *) tmp + sizeof(SERVER_SCARD_IO_REQUEST)), linkedLen); memcpy(tmp, pioSendPci, sizeof(SERVER_SCARD_IO_REQUEST)); SC_xfree(&lcHandle, pioSendPci); pioSendPci = tmp; tmp = NULL; } else pioSendPci->cbPciLength = sizeof(SERVER_SCARD_IO_REQUEST); if (map[3] & INPUT_LINKED) { in_uint32_le(in, linkedLen); sendBuf = SC_xmalloc(&lcHandle, linkedLen); if (!sendBuf) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, sendBuf, linkedLen); inRepos(in, linkedLen); } else sendBuf = NULL; if (cbRecvLength) { recvBuf = SC_xmalloc(&lcHandle, cbRecvLength); if (!recvBuf) return SC_returnNoMemoryError(&lcHandle, in, out); } if (map[4] & INPUT_LINKED) { pioRecvPci = SC_xmalloc(&lcHandle, sizeof(SERVER_SCARD_IO_REQUEST)); if (!pioRecvPci) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, pioRecvPci, sizeof(SERVER_SCARD_IO_REQUEST)); in_uint32_le(in, map[6]); if (map[6] & INPUT_LINKED) { in_uint32_le(in, linkedLen); pioRecvPci->cbPciLength = linkedLen + sizeof(SERVER_SCARD_IO_REQUEST); tmp = SC_xmalloc(&lcHandle, pioRecvPci->cbPciLength); if (!tmp) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, (void *) ((unsigned char *) tmp + sizeof(SERVER_SCARD_IO_REQUEST)), linkedLen); memcpy(tmp, pioRecvPci, sizeof(SERVER_SCARD_IO_REQUEST)); SC_xfree(&lcHandle, pioRecvPci); pioRecvPci = tmp; tmp = NULL; } else pioRecvPci->cbPciLength = sizeof(SERVER_SCARD_IO_REQUEST); } else pioRecvPci = NULL; logger(SmartCard, Debug, "TS_SCardTransmit(), 0x%08x [0x%08lx], send: %d bytes, recv: %d bytes", (unsigned) hCard, (unsigned long) myHCard, (int) cbSendLength, (int) cbRecvLength); myCbRecvLength = cbRecvLength; myPioSendPci = SC_xmalloc(&lcHandle, sizeof(MYPCSC_SCARD_IO_REQUEST) + pioSendPci->cbPciLength - sizeof(SERVER_SCARD_IO_REQUEST)); if (!myPioSendPci) return SC_returnNoMemoryError(&lcHandle, in, out); copyIORequest_ServerToMyPCSC(pioSendPci, myPioSendPci); /* always a send, not always a recv */ if (pioRecvPci) { myPioRecvPci = SC_xmalloc(&lcHandle, sizeof(MYPCSC_SCARD_IO_REQUEST) + pioRecvPci->cbPciLength - sizeof(SERVER_SCARD_IO_REQUEST)); if (!myPioRecvPci) return SC_returnNoMemoryError(&lcHandle, in, out); copyIORequest_ServerToMyPCSC(pioRecvPci, myPioRecvPci); } else { myPioRecvPci = NULL; } rv = SCardTransmit(myHCard, myPioSendPci, sendBuf, (MYPCSC_DWORD) cbSendLength, myPioRecvPci, recvBuf, &myCbRecvLength); cbRecvLength = myCbRecvLength; if (pioRecvPci) { /* * pscs-lite mishandles this structure in some cases. * make sure we only copy it if it is valid. */ if (myPioRecvPci->cbPciLength >= sizeof(MYPCSC_SCARD_IO_REQUEST)) copyIORequest_MyPCSCToServer(myPioRecvPci, pioRecvPci); } if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardTransmit(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardTransmit(), success, %d bytes", (int) cbRecvLength); #if 0 if ((pioRecvPci != NULL) && (mypioRecvPci->cbPciLength > 0)) { out_uint32_le(out, (DWORD) pioRecvPci); /* if not NULL, this 4 bytes indicates that pioRecvPci is present */ } else #endif out_uint32_le(out, 0); /* pioRecvPci 0x00; */ outBufferStart(out, cbRecvLength); /* start of recvBuf output */ #if 0 if ((pioRecvPci) && (mypioRecvPci->cbPciLength > 0)) { out_uint32_le(out, mypioRecvPci->dwProtocol); int len = mypioRecvPci->cbPciLength - sizeof(mypioRecvPci); outBufferStartWithLimit(out, len, 12); outBufferFinishWithLimit(out, (char *) ((DWORD) pioRecvPci + sizeof(pioRecvPci)), len, 12); } #endif outBufferFinish(out, (char *) recvBuf, cbRecvLength); } outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static MYPCSC_DWORD TS_SCardStatus(STREAM in, STREAM out, RD_BOOL wide) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; SERVER_DWORD dwState = 0, dwProtocol = 0, dwReaderLen, dwAtrLen; MYPCSC_DWORD state, protocol, readerLen, atrLen; SERVER_DWORD dataLength; PMEM_HANDLE lcHandle = NULL; char *readerName; unsigned char *atr; in_uint8s(in, 0x24); in_uint32_le(in, dwReaderLen); in_uint32_le(in, dwAtrLen); in_uint8s(in, 0x0C); in_uint32_le(in, hCard); in_uint8s(in, 0x04); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardStatus(), hcard: 0x%08x [0x%08lx], reader len: %d bytes, atr len: %d bytes", (unsigned) hCard, (unsigned long) myHCard, (int) dwReaderLen, (int) dwAtrLen); if (dwReaderLen == 0 || dwReaderLen == (SERVER_DWORD) SCARD_AUTOALLOCATE || dwReaderLen > SCARD_MAX_MEM) dwReaderLen = SCARD_MAX_MEM; if (dwAtrLen == 0 || dwAtrLen == (SERVER_DWORD) SCARD_AUTOALLOCATE || dwAtrLen > SCARD_MAX_MEM) dwAtrLen = SCARD_MAX_MEM; #if 1 /* * Active client sometimes sends a readerlen *just* big enough * SCardStatus doesn't seem to like this. This is a workaround, * aka hack! */ dwReaderLen = 200; #endif readerName = SC_xmalloc(&lcHandle, dwReaderLen + 2); if (!readerName) return SC_returnNoMemoryError(&lcHandle, in, out); atr = SC_xmalloc(&lcHandle, dwAtrLen + 1); if (!atr) return SC_returnNoMemoryError(&lcHandle, in, out); state = dwState; protocol = dwProtocol; readerLen = dwReaderLen; atrLen = dwAtrLen; rv = SCardStatus(myHCard, readerName, &readerLen, &state, &protocol, atr, &atrLen); dwAtrLen = atrLen; dwReaderLen = readerLen; dwProtocol = protocol; dwState = state; if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardTransmit(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); return SC_returnCode(rv, &lcHandle, in, out); } else { logger(SmartCard, Debug, "TS_SCardTransmit(), success, state=0x%08x, proto=0x%08x", (unsigned) dwState, (unsigned) dwProtocol); if (dwState & (SCARD_SPECIFIC | SCARD_NEGOTIABLE)) dwState = 0x00000006; else #if 0 if (dwState & SCARD_SPECIFIC) dwState = 0x00000006; else if (dwState & SCARD_NEGOTIABLE) dwState = 0x00000005; else #endif if (dwState & SCARD_POWERED) dwState = 0x00000004; else if (dwState & SCARD_SWALLOWED) dwState = 0x00000003; else if (dwState & SCARD_PRESENT) dwState = 0x00000002; else if (dwState & SCARD_ABSENT) dwState = 0x00000001; else dwState = 0x00000000; size_t p_len1 = s_tell(out); out_uint32_le(out, dwReaderLen); out_uint32_le(out, 0x00020000); out_uint32_le(out, dwState); out_uint32_le(out, dwProtocol); out_uint8a(out, atr, dwAtrLen); if (dwAtrLen < 32) { out_uint8s(out, 32 - dwAtrLen); } out_uint32_le(out, dwAtrLen); size_t p_len2 = s_tell(out); out_uint32_le(out, dwReaderLen); dataLength = outString(out, readerName, wide); dataLength += outString(out, "\0", wide); outRepos(out, dataLength); s_mark_end(out); size_t psave = s_tell(out); s_seek(out, p_len1); out_uint32_le(out, dataLength); s_seek(out, p_len2); out_uint32_le(out, dataLength); s_seek(out, psave); } outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static MYPCSC_DWORD TS_SCardState(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; SERVER_DWORD dwState = 0, dwProtocol = 0, dwReaderLen, dwAtrLen; MYPCSC_DWORD state, protocol, readerLen, atrLen; PMEM_HANDLE lcHandle = NULL; char *readerName; unsigned char *atr; in_uint8s(in, 0x24); in_uint32_le(in, dwAtrLen); in_uint8s(in, 0x0C); in_uint32_le(in, hCard); in_uint8s(in, 0x04); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardState(), hcard: 0x%08x [0x%08lx], atrlen: %d bytes", (unsigned) hCard, (unsigned long) myHCard, (int) dwAtrLen); dwReaderLen = SCARD_MAX_MEM; if (dwAtrLen <= 0 || dwAtrLen == (SERVER_DWORD) SCARD_AUTOALLOCATE || dwAtrLen > SCARD_MAX_MEM) dwAtrLen = SCARD_MAX_MEM; readerName = SC_xmalloc(&lcHandle, dwReaderLen + 2); if (!readerName) return SC_returnNoMemoryError(&lcHandle, in, out); atr = SC_xmalloc(&lcHandle, dwAtrLen + 1); if (!atr) return SC_returnNoMemoryError(&lcHandle, in, out); state = dwState; protocol = dwProtocol; readerLen = dwReaderLen; atrLen = dwAtrLen; rv = SCardStatus(myHCard, readerName, &readerLen, &state, &protocol, atr, &atrLen); dwAtrLen = atrLen; dwReaderLen = readerLen; dwProtocol = protocol; dwState = state; if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardState(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); return SC_returnCode(rv, &lcHandle, in, out); } else { logger(SmartCard, Debug, "TS_SCardState(), success, state=0x%08x, proto=0x%08x", (unsigned) dwState, (unsigned) dwProtocol); if (dwState & (SCARD_SPECIFIC | SCARD_NEGOTIABLE)) dwState = 0x00000006; else #if 0 if (dwState & SCARD_SPECIFIC) dwState = 0x00000006; else if (dwState & SCARD_NEGOTIABLE) dwState = 0x00000005; else #endif if (dwState & SCARD_POWERED) dwState = 0x00000004; else if (dwState & SCARD_SWALLOWED) dwState = 0x00000003; else if (dwState & SCARD_PRESENT) dwState = 0x00000002; else if (dwState & SCARD_ABSENT) dwState = 0x00000001; else dwState = 0x00000000; out_uint32_le(out, dwState); out_uint32_le(out, dwProtocol); out_uint32_le(out, dwAtrLen); out_uint32_le(out, 0x00000001); out_uint32_le(out, dwAtrLen); out_uint8a(out, atr, dwAtrLen); outRepos(out, dwAtrLen); } outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } #ifndef WITH_PCSC120 /* Currently unused */ #if 0 static MYPCSC_DWORD TS_SCardListReaderGroups(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; SERVER_DWORD dwGroups; MYPCSC_DWORD groups; char *szGroups; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x20); in_uint32_le(in, dwGroups); in_uint8s(in, 0x04); in_uint32_le(in, hContext); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardListReaderGroups(), ontext: 0x%08x [0x%08lx], groups: %d", (unsigned) hContext, (unsigned int) myHContext, (int) dwGroups); if (dwGroups <= 0 || dwGroups == SCARD_AUTOALLOCATE || dwGroups > SCARD_MAX_MEM) dwGroups = SCARD_MAX_MEM; szGroups = SC_xmalloc(&lcHandle, dwGroups); if (!szGroups) return SC_returnNoMemoryError(&lcHandle, in, out); groups = dwGroups; rv = SCardListReaderGroups(myHContext, szGroups, &groups); dwGroups = groups; if (rv) { logger(SmartCard, Error, "TS_SCardListReaderGroups(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); return SC_returnCode(rv, &lcHandle, in, out); } else { int i; char *cur; logger(SmartCard, Debug, "TS_SCardListReaderGroups(), success"); for (i = 0, cur = szGroups; i < dwGroups; i++, cur += strlen(cur) + 1) { logger(SmartCard, Debug, "TS_SCardListReaderGroups(), %s", cur); } } out_uint32_le(out, dwGroups); out_uint32_le(out, 0x00200000); out_uint32_le(out, dwGroups); out_uint8a(out, szGroups, dwGroups); outRepos(out, dwGroups); out_uint32_le(out, 0x00000000); outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } #endif static MYPCSC_DWORD TS_SCardGetAttrib(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; SERVER_DWORD dwAttrId, dwAttrLen; MYPCSC_DWORD attrLen; unsigned char *pbAttr; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x20); in_uint32_le(in, dwAttrId); in_uint8s(in, 0x04); in_uint32_le(in, dwAttrLen); in_uint8s(in, 0x0C); in_uint32_le(in, hCard); myHCard = _scard_handle_list_get_pcsc_handle(hCard); logger(SmartCard, Debug, "TS_SCardGetAttrib(), hcard: 0x%08x [0x%08lx], attrib: 0x%08x (%d bytes)", (unsigned) hCard, (unsigned long) myHCard, (unsigned) dwAttrId, (int) dwAttrLen); pbAttr = NULL; if (dwAttrLen != (SERVER_DWORD) SCARD_AUTOALLOCATE) { if (dwAttrLen > MAX_BUFFER_SIZE) { dwAttrLen = MAX_BUFFER_SIZE; } pbAttr = SC_xmalloc(&lcHandle, dwAttrLen); if (!pbAttr) return SC_returnNoMemoryError(&lcHandle, in, out); } attrLen = dwAttrLen; rv = SCardGetAttrib(myHCard, (MYPCSC_DWORD) dwAttrId, pbAttr, &attrLen); dwAttrLen = attrLen; if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardGetAttrib(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); return SC_returnCode(rv, &lcHandle, in, out); } else { logger(SmartCard, Debug, "TS_SCardGetAttrib(), %d bytes", (int) dwAttrLen); out_uint32_le(out, dwAttrLen); out_uint32_le(out, 0x00000200); out_uint32_le(out, dwAttrLen); if (!pbAttr) { out_uint8s(out, dwAttrLen); } else { out_uint8a(out, pbAttr, dwAttrLen); } outRepos(out, dwAttrLen); out_uint32_le(out, 0x00000000); } outForceAlignment(out, 8); s_mark_end(out); return rv; } /* Currently unused */ #if 0 static MYPCSC_DWORD TS_SCardSetAttrib(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hCard; MYPCSC_SCARDCONTEXT myHCard; SERVER_DWORD dwAttrId; SERVER_DWORD dwAttrLen; unsigned char *pbAttr; PMEM_HANDLE lcHandle = NULL; in_uint8s(in, 0x20); in_uint32_le(in, dwAttrId); in_uint8s(in, 0x04); in_uint32_le(in, dwAttrLen); in_uint8s(in, 0x0C); in_uint32_le(in, hCard); myHCard = scHandleToMyPCSC(hCard); logger(SmartCard, Debug, "TS_SCardSetAttrib(), hcard: 0x%08x [0x%08lx], attrib: 0x%08x (%d bytes)", (unsigned) hCard, (unsigned long) myHCard, (unsigned) dwAttrId, (int) dwAttrLen); if (dwAttrLen > MAX_BUFFER_SIZE) dwAttrLen = MAX_BUFFER_SIZE; pbAttr = SC_xmalloc(&lcHandle, dwAttrLen); if (!pbAttr) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, pbAttr, dwAttrLen); rv = SCardSetAttrib(myHCard, (MYPCSC_DWORD) dwAttrId, pbAttr, (MYPCSC_DWORD) dwAttrLen); if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Error, "TS_SCardSetAttrib(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardSetAttrib(), success"); } out_uint32_le(out, 0x00000000); out_uint32_le(out, 0x00000200); out_uint32_le(out, 0x00000000); out_uint32_le(out, 0x00000000); outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } #endif #endif static MYPCSC_DWORD TS_SCardControl(STREAM in, STREAM out) { MYPCSC_DWORD rv; SERVER_SCARDCONTEXT hContext; MYPCSC_SCARDCONTEXT myHContext; SERVER_SCARDHANDLE hCard; MYPCSC_SCARDHANDLE myHCard; SERVER_DWORD map[3]; SERVER_DWORD dwControlCode; unsigned char *pInBuffer, *pOutBuffer; SERVER_DWORD nInBufferSize, nOutBufferSize, nOutBufferRealSize, nBytesReturned; MYPCSC_DWORD sc_nBytesReturned; PMEM_HANDLE lcHandle = NULL; pInBuffer = NULL; pOutBuffer = NULL; in_uint8s(in, 0x14); in_uint32_le(in, map[0]); in_uint8s(in, 0x04); in_uint32_le(in, map[1]); in_uint32_le(in, dwControlCode); in_uint32_le(in, nInBufferSize); in_uint32_le(in, map[2]); in_uint8s(in, 0x04); in_uint32_le(in, nOutBufferSize); in_uint8s(in, 0x04); in_uint32_le(in, hContext); in_uint8s(in, 0x04); in_uint32_le(in, hCard); if (map[2] & INPUT_LINKED) { /* read real input size */ in_uint32_le(in, nInBufferSize); if (nInBufferSize > 0) { pInBuffer = SC_xmalloc(&lcHandle, nInBufferSize); if (!pInBuffer) return SC_returnNoMemoryError(&lcHandle, in, out); in_uint8a(in, pInBuffer, nInBufferSize); } } myHCard = _scard_handle_list_get_pcsc_handle(hCard); myHContext = _scard_handle_list_get_pcsc_handle(hContext); logger(SmartCard, Debug, "TS_SCardControl(), context: 0x%08x [0x%08lx], hcard: 0x%08x [0x%08lx], code: 0x%08x, in: %d bytes, out: %d bytes)\n", (unsigned) hContext, (unsigned long) myHContext, (unsigned) hCard, (unsigned long) myHCard, (unsigned) dwControlCode, (int) nInBufferSize, (int) nOutBufferSize); /* Is this a proper Windows smart card ioctl? */ /* FILE_DEVICE_SMARTCARD defined as 0x000031 in Winsmcrd.h */ if ((dwControlCode & 0xffff0000) == (49 << 16)) { /* Translate to local encoding */ dwControlCode = (dwControlCode & 0x3ffc) >> 2; dwControlCode = SCARD_CTL_CODE(dwControlCode); } else { /* * According to "Interoperability Specification for ICCs and Personal Computer Systems. * Part 10 IFDs with Secure PIN Entry Capabilities Supplement - IFDs with Feature Capabilities" * you have to issue GET_FEATURE_REQUEST to get codes for supported features. * * You have to use only those codes not some hardcoded values. * * So the correct way to do fix this would be to parse received TLV from * CM_IOCTL_GET_FEATURE_REQUEST and get all codes for the corresponding features for * the particular reader as these values are defined by the driver itself and not * by pcsclite. * */ /* ATM, I removed features code transformations */ /* You know what to do if any problem arises in the future. */ if ((dwControlCode & 0xff000000) != SCARD_CTL_CODE(0)) { logger(SmartCard, Warning, "TS_SCardControl(), bogus smart card control code 0x%08x", dwControlCode); } } #if 0 if (nOutBufferSize > 0) { nOutBufferRealSize = nOutBufferSize; } else #endif nOutBufferRealSize = 1024; nBytesReturned = nOutBufferRealSize; nBytesReturned = nOutBufferRealSize; pOutBuffer = SC_xmalloc(&lcHandle, nOutBufferRealSize); if (!pOutBuffer) return SC_returnNoMemoryError(&lcHandle, in, out); sc_nBytesReturned = nBytesReturned; #ifdef WITH_PCSC120 rv = SCardControl(myHCard, pInBuffer, (MYPCSC_DWORD) nInBufferSize, pOutBuffer, &sc_nBytesReturned); #else rv = SCardControl(myHCard, (MYPCSC_DWORD) dwControlCode, pInBuffer, (MYPCSC_DWORD) nInBufferSize, pOutBuffer, (MYPCSC_DWORD) nOutBufferRealSize, &sc_nBytesReturned); #endif nBytesReturned = sc_nBytesReturned; if (rv != SCARD_S_SUCCESS) { logger(SmartCard, Debug, "TS_SCardControl(), failed: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); } else { logger(SmartCard, Debug, "TS_SCardControl(), success, out: %d bytes", (int) nBytesReturned); } out_uint32_le(out, nBytesReturned); out_uint32_le(out, 0x00000004); out_uint32_le(out, nBytesReturned); if (nBytesReturned > 0) { out_uint8a(out, pOutBuffer, nBytesReturned); outRepos(out, nBytesReturned); } outForceAlignment(out, 8); s_mark_end(out); SC_xfreeallmemory(&lcHandle); return rv; } static MYPCSC_DWORD TS_SCardAccessStartedEvent(STREAM in, STREAM out) { UNUSED(in); logger(SmartCard, Debug, "TS_SCardAccessStartedEvent()"); out_uint8s(out, 8); return SCARD_S_SUCCESS; } static RD_NTSTATUS scard_device_control(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out, uint32 srv_buf_len) { UNUSED(handle); SERVER_DWORD Result = 0x00000000; size_t psize, pend, pStatusCode; SERVER_DWORD addToEnd = 0; /* Processing request */ /* See MS-RSPESC 1.3 Overview for protocol flow */ /* Set CommonTypeHeader (MS-RPCE 2.2.6.1) */ out_uint32_le(out, 0x00081001); /* Header lines */ out_uint32_le(out, 0xCCCCCCCC); psize = s_tell(out); /* Set PrivateTypeHeader (MS-RPCE 2.2.6.2) */ out_uint32_le(out, 0x00000000); /* Size of data portion */ out_uint32_le(out, 0x00000000); /* Zero bytes (may be useful) */ pStatusCode = s_tell(out); out_uint32_le(out, 0x00000000); /* Status Code */ switch (request) { /* SCardEstablishContext */ case SC_ESTABLISH_CONTEXT: { Result = (SERVER_DWORD) TS_SCardEstablishContext(in, out); break; } /* SCardReleaseContext */ case SC_RELEASE_CONTEXT: { Result = (SERVER_DWORD) TS_SCardReleaseContext(in, out); break; } /* SCardIsValidContext */ case SC_IS_VALID_CONTEXT: { Result = (SERVER_DWORD) TS_SCardIsValidContext(in, out); break; } /* SCardListReaders */ case SC_LIST_READERS: /* SCardListReadersA */ case SC_LIST_READERS + 4: /* SCardListReadersW */ { RD_BOOL wide = request != SC_LIST_READERS; Result = (SERVER_DWORD) TS_SCardListReaders(in, out, wide); break; } /* ScardConnect */ case SC_CONNECT: /* ScardConnectA */ case SC_CONNECT + 4: /* SCardConnectW */ { RD_BOOL wide = request != SC_CONNECT; Result = (SERVER_DWORD) TS_SCardConnect(in, out, wide); break; } /* ScardReconnect */ case SC_RECONNECT: { Result = (SERVER_DWORD) TS_SCardReconnect(in, out); break; } /* ScardDisconnect */ case SC_DISCONNECT: { Result = (SERVER_DWORD) TS_SCardDisconnect(in, out); break; } /* ScardGetStatusChange */ case SC_GET_STATUS_CHANGE: /* SCardGetStatusChangeA */ case SC_GET_STATUS_CHANGE + 4: /* SCardGetStatusChangeW */ { RD_BOOL wide = request != SC_GET_STATUS_CHANGE; Result = (SERVER_DWORD) TS_SCardGetStatusChange(in, out, wide); break; } /* SCardCancel */ case SC_CANCEL: { Result = (SERVER_DWORD) TS_SCardCancel(in, out); break; } /* SCardLocateCardsByATR */ case SC_LOCATE_CARDS_BY_ATR: /* SCardLocateCardsByATRA */ case SC_LOCATE_CARDS_BY_ATR + 4: /* SCardLocateCardsByATRW */ { RD_BOOL wide = request != SC_LOCATE_CARDS_BY_ATR; Result = (SERVER_DWORD) TS_SCardLocateCardsByATR(in, out, wide); break; } /* SCardBeginTransaction */ case SC_BEGIN_TRANSACTION: { Result = (SERVER_DWORD) TS_SCardBeginTransaction(in, out); break; } /* SCardBeginTransaction */ case SC_END_TRANSACTION: { Result = (SERVER_DWORD) TS_SCardEndTransaction(in, out); break; } /* ScardTransmit */ case SC_TRANSMIT: { Result = (SERVER_DWORD) TS_SCardTransmit(in, out, srv_buf_len); break; } /* SCardControl */ case SC_CONTROL: { Result = (SERVER_DWORD) TS_SCardControl(in, out); break; } /* SCardGetAttrib */ #ifndef WITH_PCSC120 case SC_GETATTRIB: { Result = (SERVER_DWORD) TS_SCardGetAttrib(in, out); break; } #endif case SC_ACCESS_STARTED_EVENT: { Result = (SERVER_DWORD) TS_SCardAccessStartedEvent(in, out); break; } case SC_STATUS: /* SCardStatusA */ case SC_STATUS + 4: /* SCardStatusW */ { RD_BOOL wide = request != SC_STATUS; Result = (SERVER_DWORD) TS_SCardStatus(in, out, wide); break; } case SC_STATE: /* SCardState */ { Result = (SERVER_DWORD) TS_SCardState(in, out); break; } default: { logger(SmartCard, Warning, "scard_device_control(), unhandled operation %d", (int) request); Result = 0x80100014; out_uint8s(out, 256); break; } } #if 0 out_uint32_le(out, 0x00000000); #endif s_mark_end(out); /* Setting modified variables */ pend = s_tell(out); /* setting data size */ s_seek(out, psize); out_uint32_le(out, pend - psize - 16); /* setting status code */ s_seek(out, pStatusCode); out_uint32_le(out, Result); /* finish */ s_seek(out, pend); /* TODO: Check MS-RPCE 2.2.6.2 for alignment requirements (IIRC length must be integral multiple of 8) */ addToEnd = (pend - pStatusCode) % 16; if (addToEnd < 16 && addToEnd > 0) { out_uint8s(out, addToEnd); s_mark_end(out); } if (Result == SCARD_E_INSUFFICIENT_BUFFER) return RD_STATUS_BUFFER_TOO_SMALL; return RD_STATUS_SUCCESS; } /* Thread functions */ static STREAM duplicateStream(PMEM_HANDLE * handle, STREAM s, uint32 buffer_size, RD_BOOL isInputStream) { // FIXME: Shouldn't be allocating streams manually STREAM d = SC_xmalloc(handle, sizeof(struct stream)); if (d != NULL) { if (isInputStream) d->size = (size_t) (s->end) - (size_t) (s->data); else if (buffer_size < s->size) d->size = s->size; else d->size = buffer_size; d->data = SC_xmalloc(handle, d->size); d->end = (void *) ((size_t) (d->data) + (size_t) (s->end) - (size_t) (s->data)); d->p = (void *) ((size_t) (d->data) + (size_t) (s->p) - (size_t) (s->data)); d->iso_hdr = (void *) ((size_t) (d->data) + (size_t) (s->iso_hdr) - (size_t) (s->data)); d->mcs_hdr = (void *) ((size_t) (d->data) + (size_t) (s->mcs_hdr) - (size_t) (s->data)); d->sec_hdr = (void *) ((size_t) (d->data) + (size_t) (s->sec_hdr) - (size_t) (s->data)); d->sec_hdr = (void *) ((size_t) (d->data) + (size_t) (s->sec_hdr) - (size_t) (s->data)); d->rdp_hdr = (void *) ((size_t) (d->data) + (size_t) (s->rdp_hdr) - (size_t) (s->data)); d->channel_hdr = (void *) ((size_t) (d->data) + (size_t) (s->channel_hdr) - (size_t) (s->data)); if (isInputStream) memcpy(d->data, s->data, (size_t) (s->end) - (size_t) (s->data)); else memcpy(d->data, s->data, (size_t) (s->p) - (size_t) (s->data)); } return d; } /* Currently unused */ #if 0 static void freeStream(PMEM_HANDLE * handle, STREAM s) { if (s != NULL) { if (s->data != NULL) SC_xfree(handle, s->data); SC_xfree(handle, s); } } #endif static PSCThreadData SC_addToQueue(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out) { PMEM_HANDLE lcHandle = NULL; PSCThreadData data = SC_xmalloc(&lcHandle, sizeof(TSCThreadData)); if (!data) return NULL; else { data->memHandle = lcHandle; data->device = curDevice; data->id = curId; data->epoch = curEpoch; data->handle = handle; data->request = request; data->srv_buf_len = curBytesOut - 0x14; data->in = duplicateStream(&(data->memHandle), in, 0, SC_TRUE); if (data->in == NULL) { SC_xfreeallmemory(&(data->memHandle)); return NULL; } data->out = duplicateStream(&(data->memHandle), out, OUT_STREAM_SIZE + curBytesOut, SC_FALSE); if (data->out == NULL) { SC_xfreeallmemory(&(data->memHandle)); return NULL; } data->next = NULL; pthread_mutex_lock(&queueAccess); if (queueLast) queueLast->next = data; queueLast = data; if (!queueFirst) queueFirst = data; pthread_cond_broadcast(&queueEmpty); pthread_mutex_unlock(&queueAccess); } return data; } static void SC_destroyThreadData(PSCThreadData data) { if (data) { PMEM_HANDLE handle = data->memHandle; SC_xfreeallmemory(&handle); } } static PSCThreadData SC_getNextInQueue() { PSCThreadData Result = NULL; pthread_mutex_lock(&queueAccess); while (queueFirst == NULL) pthread_cond_wait(&queueEmpty, &queueAccess); Result = queueFirst; queueFirst = queueFirst->next; if (!queueFirst) { queueLast = NULL; } Result->next = NULL; pthread_mutex_unlock(&queueAccess); return Result; } static void SC_deviceControl(PSCThreadData data) { RD_NTSTATUS status; size_t buffer_len = 0; status = scard_device_control(data->handle, data->request, data->in, data->out, data->srv_buf_len); buffer_len = s_tell(data->out); /* if iorequest belongs to another epoch, don't send response back to server due to it's considered as abandoned. */ if (data->epoch == curEpoch) { uint8 *buf; s_seek(data->out, 0); in_uint8p(data->out, buf, buffer_len); rdpdr_send_completion(data->device, data->id, status, buffer_len, buf, buffer_len); } SC_destroyThreadData(data); } static void * thread_function(PThreadListElement listElement) { pthread_mutex_lock(&listElement->busy); while (1) { while (listElement->data == NULL) pthread_cond_wait(&listElement->nodata, &listElement->busy); SC_deviceControl(listElement->data); listElement->data = NULL; } pthread_mutex_unlock(&listElement->busy); pthread_exit(NULL); return NULL; } static void SC_handleRequest(PSCThreadData data) { int Result = 0; PThreadListElement cur; for (cur = threadList; cur != NULL; cur = cur->next) { if (cur->data == NULL) { pthread_mutex_lock(&cur->busy); /* double check with lock held.... */ if (cur->data != NULL) { pthread_mutex_unlock(&cur->busy); continue; } /* Wake up thread */ cur->data = data; pthread_cond_broadcast(&cur->nodata); pthread_mutex_unlock(&cur->busy); return; } } cur = SC_xmalloc(&threadListHandle, sizeof(TThreadListElement)); if (!cur) return; threadCount++; pthread_mutex_init(&cur->busy, NULL); pthread_cond_init(&cur->nodata, NULL); cur->data = data; Result = pthread_create(&cur->thread, NULL, (void *(*)(void *)) thread_function, cur); if (0 != Result) { logger(SmartCard, Debug, "SC_handleRequest(), pthread_create() failed with 0x%.8x", Result); SC_xfree(&threadListHandle, cur); SC_destroyThreadData(data); data = NULL; } cur->next = threadList; threadList = cur; } static void * queue_handler_function(void *data) { UNUSED(data); PSCThreadData cur_data = NULL; while (1) { cur_data = SC_getNextInQueue(); switch (cur_data->request) { case SC_ESTABLISH_CONTEXT: case SC_RELEASE_CONTEXT: { SC_deviceControl(cur_data); break; } default: { SC_handleRequest(cur_data); break; } } } return NULL; } static RD_NTSTATUS thread_wrapper(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out) { if (SC_addToQueue(handle, request, in, out)) return RD_STATUS_PENDING | 0xC0000000; else return RD_STATUS_NO_SUCH_FILE; } DEVICE_FNS scard_fns = { scard_create, scard_close, scard_read, scard_write, thread_wrapper }; void scard_lock(int lock) { if (!scard_mutex) { int i; scard_mutex = (pthread_mutex_t **) xmalloc(sizeof(pthread_mutex_t *) * SCARD_LOCK_LAST); for (i = 0; i < SCARD_LOCK_LAST; i++) { scard_mutex[i] = NULL; } } if (!scard_mutex[lock]) { scard_mutex[lock] = (pthread_mutex_t *) xmalloc(sizeof(pthread_mutex_t)); pthread_mutex_init(scard_mutex[lock], NULL); } pthread_mutex_lock(scard_mutex[lock]); } void scard_unlock(int lock) { pthread_mutex_unlock(scard_mutex[lock]); } void scard_reset_state() { curDevice = 0; curId = 0; curBytesOut = 0; queueFirst = queueLast = NULL; } void scard_release_all_contexts(void) { _scard_handle_list_t *item, *next; item = g_scard_handle_list; while (item) { /* Cancelling ScardGetStatusChange calls */ SCardCancel(item->handle); /* releasing context to end all transactions on it */ SCardReleaseContext(item->handle); next = item->next; xfree(item); item = next; } g_scard_handle_list = NULL; }