96faa24bd5
The proper thing to do would be to forward the errors to the server and let it deal with it. No need to present these as errors to the user.
2755 lines
68 KiB
C
2755 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 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
|
|
|
|
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>
|
|
#ifndef MAKE_PROTO
|
|
#ifdef __APPLE__
|
|
#include <PCSC/wintypes.h>
|
|
#include <PCSC/pcsclite.h>
|
|
#include <PCSC/winscard.h>
|
|
#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 */
|
|
|
|
#endif /* MAKE_PROTO */
|
|
void
|
|
scardSetInfo(uint32 epoch, uint32 device, uint32 id, uint32 bytes_out)
|
|
{
|
|
curDevice = device;
|
|
curId = id;
|
|
curBytesOut = bytes_out;
|
|
curEpoch = epoch;
|
|
}
|
|
|
|
#ifndef MAKE_PROTO
|
|
|
|
static RD_NTSTATUS
|
|
scard_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition,
|
|
uint32 flags_and_attributes, char *filename, RD_NTHANDLE * phandle)
|
|
{
|
|
return RD_STATUS_SUCCESS;
|
|
}
|
|
|
|
static RD_NTSTATUS
|
|
scard_close(RD_NTHANDLE handle)
|
|
{
|
|
return RD_STATUS_SUCCESS;
|
|
}
|
|
|
|
static RD_NTSTATUS
|
|
scard_read(RD_NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
|
|
{
|
|
return RD_STATUS_SUCCESS;
|
|
}
|
|
|
|
static RD_NTSTATUS
|
|
scard_write(RD_NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
|
|
{
|
|
return RD_STATUS_SUCCESS;
|
|
}
|
|
#endif /* MAKE_PROTO */
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#ifndef MAKE_PROTO
|
|
typedef struct _scard_handle_list_t
|
|
{
|
|
struct _scard_handle_list_t *next;
|
|
/* pcsc handles is datatype long which
|
|
is arch sizedependent */
|
|
long handle;
|
|
/* rdp server handles are always 32bit */
|
|
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 dont 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)
|
|
{
|
|
int header = (length < 0) ? (0) : ((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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
MYPCSC_DWORD rv;
|
|
MYPCSC_SCARDCONTEXT myHContext;
|
|
SERVER_SCARDCONTEXT hContext;
|
|
char *readers = NULL;
|
|
DWORD readerCount = 1024;
|
|
|
|
hContext = 0;
|
|
|
|
/* code segment */
|
|
logger(SmartCard, Debug, "TS_SCardEstablishContext()");
|
|
|
|
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &myHContext);
|
|
if (rv != SCARD_S_SUCCESS)
|
|
{
|
|
logger(SmartCard, Error, "TS_SCardEstablishContext(), failed: %s (0x%08x)",
|
|
pcsc_stringify_error(rv), (unsigned int) rv);
|
|
goto bail_out;
|
|
}
|
|
|
|
if (!myHContext)
|
|
{
|
|
logger(SmartCard, Error, "TS_SCardEstablishedContext(), myHContext == NULL");
|
|
goto bail_out;
|
|
}
|
|
|
|
/* This is a workaround where windows application generally
|
|
behaves better with polling of smartcard subsystem
|
|
availability than were there are no readers available. */
|
|
rv = SCardListReaders(myHContext, NULL, readers, &readerCount);
|
|
if (rv != SCARD_S_SUCCESS)
|
|
{
|
|
logger(SmartCard, Debug,
|
|
"TS_SCardEstablishContext(), No readers connected, return no service to client.");
|
|
rv = SCARD_E_NO_SERVICE;
|
|
SCardReleaseContext(myHContext);
|
|
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, Error, "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;
|
|
char *readers;
|
|
DWORD readerCount = 1024;
|
|
PMEM_HANDLE lcHandle = NULL;
|
|
|
|
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);
|
|
|
|
/* There is no realization of SCardIsValidContext in PC/SC Lite so we call SCardListReaders */
|
|
|
|
readers = SC_xmalloc(&lcHandle, 1024);
|
|
if (!readers)
|
|
return SC_returnNoMemoryError(&lcHandle, in, out);
|
|
|
|
rv = SCardListReaders(myHContext, NULL, readers, &readerCount);
|
|
|
|
if (rv)
|
|
{
|
|
logger(SmartCard, Error, "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);
|
|
SC_xfreeallmemory(&lcHandle);
|
|
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, Error, "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, Error, "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, Error, "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 (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0)
|
|
cur->dwCurrentState |= SCARD_STATE_IGNORE;
|
|
}
|
|
|
|
logger(SmartCard, Debug,
|
|
"TS_SCardGetStatusChange(), reader='%s', user=%p, state=%x%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 pcsclite, 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);
|
|
|
|
switch (rv)
|
|
{
|
|
case SCARD_S_SUCCESS:
|
|
case SCARD_E_TIMEOUT:
|
|
break;
|
|
default:
|
|
logger(SmartCard, Warning,
|
|
"TS_SCardGetStatusChange(), unhandled error from SCardGetStatusChange: %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=%x%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, Error, "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)
|
|
{
|
|
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=%x%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, Error, "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=%x%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, Error, "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, Error, "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)
|
|
{
|
|
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);
|
|
|
|
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;
|
|
|
|
/* FIXME: handle responses with length > 448 bytes */
|
|
if (cbRecvLength > 448)
|
|
{
|
|
logger(SmartCard, Warning,
|
|
"TS_SCardTransmit(), card response limit reached, %d truncated to 448 bytes",
|
|
cbRecvLength);
|
|
cbRecvLength = 448;
|
|
}
|
|
|
|
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, Error, "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 == SCARD_AUTOALLOCATE || dwReaderLen > SCARD_MAX_MEM)
|
|
dwReaderLen = SCARD_MAX_MEM;
|
|
if (dwAtrLen <= 0 || dwAtrLen == 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, Error, "TS_SCardTransmit(), failed: %s (0x%08x)",
|
|
pcsc_stringify_error(rv), (unsigned int) rv);
|
|
return SC_returnCode(rv, &lcHandle, in, out);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
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 == 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, Error, "TS_SCardState(), failed: %s (0x%08x)",
|
|
pcsc_stringify_error(rv), (unsigned int) rv);
|
|
return SC_returnCode(rv, &lcHandle, in, out);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
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);
|
|
|
|
if (dwAttrLen > MAX_BUFFER_SIZE)
|
|
dwAttrLen = MAX_BUFFER_SIZE;
|
|
|
|
|
|
if (dwAttrLen > SCARD_AUTOALLOCATE)
|
|
pbAttr = NULL;
|
|
else if ((dwAttrLen < 0) || (dwAttrLen > SCARD_MAX_MEM))
|
|
{
|
|
dwAttrLen = (SERVER_DWORD) SCARD_AUTOALLOCATE;
|
|
pbAttr = NULL;
|
|
}
|
|
else
|
|
{
|
|
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, Error, "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? */
|
|
if ((dwControlCode & 0xffff0000) == (49 << 16))
|
|
{
|
|
/* Translate to local encoding */
|
|
dwControlCode = (dwControlCode & 0x3ffc) >> 2;
|
|
dwControlCode = SCARD_CTL_CODE(dwControlCode);
|
|
}
|
|
else
|
|
{
|
|
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, Error, "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);
|
|
}
|
|
|
|
#ifdef PCSCLITE_VERSION_NUMBER
|
|
if (dwControlCode == SCARD_CTL_CODE(3400))
|
|
{
|
|
int i;
|
|
SERVER_DWORD cc;
|
|
|
|
for (i = 0; i < nBytesReturned / 6; i++)
|
|
{
|
|
memcpy(&cc, pOutBuffer + 2 + i * 6, 4);
|
|
cc = ntohl(cc);
|
|
cc = cc - 0x42000000;
|
|
cc = (49 << 16) | (cc << 2);
|
|
cc = htonl(cc);
|
|
memcpy(pOutBuffer + 2 + i * 6, &cc, 4);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
SERVER_DWORD Result = 0x00000000;
|
|
unsigned char *psize, *pend, *pStatusCode;
|
|
SERVER_DWORD addToEnd = 0;
|
|
|
|
/* Processing request */
|
|
|
|
out_uint32_le(out, 0x00081001); /* Header lines */
|
|
out_uint32_le(out, 0xCCCCCCCC);
|
|
psize = out->p;
|
|
out_uint32_le(out, 0x00000000); /* Size of data portion */
|
|
out_uint32_le(out, 0x00000000); /* Zero bytes (may be usefull) */
|
|
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);
|
|
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;
|
|
|
|
addToEnd = (pend - pStatusCode) % 16;
|
|
if (addToEnd < 16 && addToEnd > 0)
|
|
{
|
|
out_uint8s(out, addToEnd);
|
|
}
|
|
|
|
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->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)
|
|
{
|
|
size_t buffer_len = 0;
|
|
scard_device_control(data->handle, data->request, data->in, data->out);
|
|
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 abdonend.
|
|
*/
|
|
if (data->epoch == curEpoch)
|
|
rdpdr_send_completion(data->device, data->id, 0, 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, Error, "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)
|
|
{
|
|
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
|
|
};
|
|
#endif /* MAKE_PROTO */
|
|
|
|
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;
|
|
}
|