758f7b5156
Even though we can detect that the server buffer is too small to receive the APDU result we don't prevent the actual copy of this result to allocated buffer which results in overflow.
2762 lines
68 KiB
C
2762 lines
68 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 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, uint32 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, uint32 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;
|
|
|
|
len = strlen(alias);
|
|
strncpy(tmpMap->alias, alias, (len > 127) ? (127) : (len));
|
|
len = strlen(name);
|
|
strncpy(tmpMap->name, name, (len > 127) ? (127) : (len));
|
|
|
|
if (vendor)
|
|
{
|
|
len = strlen(vendor);
|
|
if (len > 0)
|
|
{
|
|
memset(tmpMap->vendor, 0, 128);
|
|
strncpy(tmpMap->vendor, vendor,
|
|
(len > 127) ? (127) : (len));
|
|
}
|
|
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_uint8p(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 - (out->p - out->data) % 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_uint8p(out, buffer, 2 * dataLength);
|
|
}
|
|
else
|
|
{
|
|
out_uint8p(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->p += 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);
|
|
return rv;
|
|
}
|
|
|
|
static MYPCSC_DWORD
|
|
TS_SCardReleaseContext(STREAM in, STREAM out)
|
|
{
|
|
MYPCSC_DWORD rv;
|
|
MYPCSC_SCARDCONTEXT myHContext;
|
|
SERVER_SCARDCONTEXT hContext;
|
|
|
|
in->p += 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);
|
|
return rv;
|
|
}
|
|
|
|
static MYPCSC_DWORD
|
|
TS_SCardIsValidContext(STREAM in, STREAM out)
|
|
{
|
|
MYPCSC_DWORD rv;
|
|
SERVER_SCARDCONTEXT hContext;
|
|
MYPCSC_SCARDCONTEXT myHContext;
|
|
|
|
in->p += 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);
|
|
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;
|
|
unsigned char *plen1, *plen2, *pend;
|
|
char *readers, *cur;
|
|
PMEM_HANDLE lcHandle = NULL;
|
|
|
|
in->p += 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 = out->p;
|
|
out_uint32_le(out, 0x00000000); /* Temp value for data length as 0x0 */
|
|
out_uint32_le(out, 0x01760650);
|
|
plen2 = out->p;
|
|
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);
|
|
|
|
pend = out->p;
|
|
out->p = plen1;
|
|
out_uint32_le(out, dataLength);
|
|
out->p = plen2;
|
|
out_uint32_le(out, dataLength);
|
|
out->p = pend;
|
|
|
|
outForceAlignment(out, 8);
|
|
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->p += 0x1C;
|
|
in_uint32_le(in, dwShareMode);
|
|
in_uint32_le(in, dwPreferredProtocol);
|
|
inReaderName(&lcHandle, in, &szReader, wide);
|
|
in->p += 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);
|
|
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->p += 0x20;
|
|
in_uint32_le(in, dwShareMode);
|
|
in_uint32_le(in, dwPreferredProtocol);
|
|
in_uint32_le(in, dwInitialization);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, hContext);
|
|
in->p += 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);
|
|
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->p += 0x20;
|
|
in_uint32_le(in, dwDisposition);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, hContext);
|
|
in->p += 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);
|
|
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->p += 0x18;
|
|
in_uint32_le(in, dwTimeout);
|
|
in_uint32_le(in, dwCount);
|
|
in->p += 0x08;
|
|
in_uint32_le(in, hContext);
|
|
in->p += 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->p += 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_uint8p(out, (void *) ((unsigned char **) cur + 2),
|
|
sizeof(SERVER_SCARD_READERSTATE_A) - 2 * sizeof(unsigned char *));
|
|
}
|
|
outForceAlignment(out, 8);
|
|
SC_xfreeallmemory(&lcHandle);
|
|
return rv;
|
|
}
|
|
|
|
static MYPCSC_DWORD
|
|
TS_SCardCancel(STREAM in, STREAM out)
|
|
{
|
|
MYPCSC_DWORD rv;
|
|
SERVER_SCARDCONTEXT hContext;
|
|
MYPCSC_SCARDCONTEXT myHContext;
|
|
|
|
in->p += 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);
|
|
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->p += 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_uint8p(out, (void *) ((unsigned char **) rsCur + 2),
|
|
sizeof(SCARD_READERSTATE) - 2 * sizeof(unsigned char *));
|
|
}
|
|
|
|
outForceAlignment(out, 8);
|
|
SC_xfreeallmemory(&lcHandle);
|
|
return rv;
|
|
}
|
|
|
|
static DWORD
|
|
TS_SCardBeginTransaction(STREAM in, STREAM out)
|
|
{
|
|
MYPCSC_DWORD rv;
|
|
SERVER_SCARDCONTEXT hCard;
|
|
MYPCSC_SCARDCONTEXT myHCard;
|
|
|
|
in->p += 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);
|
|
return rv;
|
|
}
|
|
|
|
static DWORD
|
|
TS_SCardEndTransaction(STREAM in, STREAM out)
|
|
{
|
|
MYPCSC_DWORD rv;
|
|
SERVER_SCARDCONTEXT hCard;
|
|
MYPCSC_SCARDCONTEXT myHCard;
|
|
SERVER_DWORD dwDisposition = 0;
|
|
|
|
in->p += 0x20;
|
|
in_uint32_le(in, dwDisposition);
|
|
in->p += 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);
|
|
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->p += 0x14;
|
|
in_uint32_le(in, map[0]);
|
|
in->p += 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->p += 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);
|
|
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->p += 0x24;
|
|
in_uint32_le(in, dwReaderLen);
|
|
in_uint32_le(in, dwAtrLen);
|
|
in->p += 0x0C;
|
|
in_uint32_le(in, hCard);
|
|
in->p += 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;
|
|
|
|
void *p_len1 = out->p;
|
|
out_uint32_le(out, dwReaderLen);
|
|
out_uint32_le(out, 0x00020000);
|
|
out_uint32_le(out, dwState);
|
|
out_uint32_le(out, dwProtocol);
|
|
out_uint8p(out, atr, dwAtrLen);
|
|
if (dwAtrLen < 32)
|
|
{
|
|
out_uint8s(out, 32 - dwAtrLen);
|
|
}
|
|
out_uint32_le(out, dwAtrLen);
|
|
|
|
void *p_len2 = out->p;
|
|
out_uint32_le(out, dwReaderLen);
|
|
dataLength = outString(out, readerName, wide);
|
|
dataLength += outString(out, "\0", wide);
|
|
outRepos(out, dataLength);
|
|
void *psave = out->p;
|
|
out->p = p_len1;
|
|
out_uint32_le(out, dataLength);
|
|
out->p = p_len2;
|
|
out_uint32_le(out, dataLength);
|
|
out->p = psave;
|
|
}
|
|
outForceAlignment(out, 8);
|
|
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->p += 0x24;
|
|
in_uint32_le(in, dwAtrLen);
|
|
in->p += 0x0C;
|
|
in_uint32_le(in, hCard);
|
|
in->p += 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_uint8p(out, atr, dwAtrLen);
|
|
outRepos(out, dwAtrLen);
|
|
}
|
|
outForceAlignment(out, 8);
|
|
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->p += 0x20;
|
|
in_uint32_le(in, dwGroups);
|
|
in->p += 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);
|
|
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->p += 0x20;
|
|
in_uint32_le(in, dwAttrId);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, dwAttrLen);
|
|
in->p += 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_uint8p(out, pbAttr, dwAttrLen);
|
|
}
|
|
outRepos(out, dwAttrLen);
|
|
out_uint32_le(out, 0x00000000);
|
|
}
|
|
outForceAlignment(out, 8);
|
|
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->p += 0x20;
|
|
in_uint32_le(in, dwAttrId);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, dwAttrLen);
|
|
in->p += 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);
|
|
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->p += 0x14;
|
|
in_uint32_le(in, map[0]);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, map[1]);
|
|
in_uint32_le(in, dwControlCode);
|
|
in_uint32_le(in, nInBufferSize);
|
|
in_uint32_le(in, map[2]);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, nOutBufferSize);
|
|
in->p += 0x04;
|
|
in_uint32_le(in, hContext);
|
|
in->p += 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_uint8p(out, pOutBuffer, nBytesReturned);
|
|
outRepos(out, nBytesReturned);
|
|
}
|
|
|
|
outForceAlignment(out, 8);
|
|
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;
|
|
unsigned char *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 = out->p;
|
|
/* 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 = out->p;
|
|
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
|
|
/* Setting modified variables */
|
|
pend = out->p;
|
|
/* setting data size */
|
|
out->p = psize;
|
|
out_uint32_le(out, pend - psize - 16);
|
|
/* setting status code */
|
|
out->p = pStatusCode;
|
|
out_uint32_le(out, Result);
|
|
/* finish */
|
|
out->p = 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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 = (size_t) data->out->p - (size_t) data->out->data;
|
|
|
|
/* if iorequest belongs to another epoch, don't send response
|
|
back to server due to it's considered as abandoned.
|
|
*/
|
|
if (data->epoch == curEpoch)
|
|
rdpdr_send_completion(data->device, data->id, status, buffer_len, data->out->data, 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;
|
|
}
|