rdesktop/scard.c
Pierre Ossman 25b8412333 Avoid poking around in STREAM internals
It's easy to make mistakes this way, and bypassed the normal bounds
checking. So make sure we always use macros or functions.
2019-05-06 14:33:38 +02:00

2791 lines
69 KiB
C

/*
rdesktop: A Remote Desktop Protocol client.
Smart Card support
Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006
Copyright 2010-2013 Pierre Ossman <ossman@cendio.se> for Cendio AB
Copyright 2011-2017 Henrik Andersson <hean01@cendio.se> for Cendio AB
Copyright 2015 Rostislav Kondratenko <r.kondratenk@wwpass.com>
Copyright 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
Copyright 2016-2018 Alexander Zakharov <uglym8@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/types.h>
#include <time.h>
#include <arpa/inet.h>
#ifdef __APPLE__
#include <PCSC/wintypes.h>
#include <PCSC/pcsclite.h>
#include <PCSC/winscard.h>
#define SCARD_CTL_CODE(code) (0x42000000 + (code))
#else
#include <wintypes.h>
#include <pcsclite.h>
#include <winscard.h>
#ifdef PCSCLITE_VERSION_NUMBER
#include <reader.h>
#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;
}