diff --git a/CMakeLists.txt b/CMakeLists.txt index c43c73981..e83f838d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,7 @@ if(NOT WIN32) find_required_package(ZLIB) find_suggested_package(ALSA) find_optional_package(PulseAudio) + find_optional_package(PCSC) find_suggested_package(Cups) find_suggested_package(FFmpeg) endif() diff --git a/channels/rdpdr/CMakeLists.txt b/channels/rdpdr/CMakeLists.txt index e79be8e7d..a05b61893 100644 --- a/channels/rdpdr/CMakeLists.txt +++ b/channels/rdpdr/CMakeLists.txt @@ -41,3 +41,7 @@ add_subdirectory(disk) add_subdirectory(printer) add_subdirectory(parallel) add_subdirectory(serial) + +if(PCSC_FOUND) + add_subdirectory(smartcard) +endif() diff --git a/channels/rdpdr/smartcard/CMakeLists.txt b/channels/rdpdr/smartcard/CMakeLists.txt new file mode 100644 index 000000000..816fc09f4 --- /dev/null +++ b/channels/rdpdr/smartcard/CMakeLists.txt @@ -0,0 +1,34 @@ +# FreeRDP: A Remote Desktop Protocol Client +# FreeRDP cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(SCARD_SRCS + scard_main.c + scard_operations.c +) + +include_directories(..) +include_directories(${PCSC_INCLUDE_DIRS}) + +add_library(scard ${SCARD_SRCS}) +set_target_properties(scard PROPERTIES PREFIX "") + +target_link_libraries(scard freerdp-utils) +target_link_libraries(scard ${PCSC_LIBRARIES}) + +install(TARGETS scard DESTINATION ${FREERDP_PLUGIN_PATH}) diff --git a/channels/rdpdr/smartcard/scard_main.c b/channels/rdpdr/smartcard/scard_main.c new file mode 100644 index 000000000..5066bf2d0 --- /dev/null +++ b/channels/rdpdr/smartcard/scard_main.c @@ -0,0 +1,204 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Redirected Smart Card Device Service + + Copyright 2011 O.S. Systems Software Ltda. + Copyright 2011 Eduardo Fiss Beloni + Copyright 2011 Anthony Tong + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include + +#include "rdpdr_types.h" +#include "rdpdr_constants.h" + +#include "scard_main.h" + + +static void +scard_free(DEVICE* dev) +{ + SCARD_DEVICE* scard = (SCARD_DEVICE*)dev; + IRP* irp; + + freerdp_thread_stop(scard->thread); + freerdp_thread_free(scard->thread); + + while ((irp = (IRP*)list_dequeue(scard->irp_list)) != NULL) + irp->Discard(irp); + list_free(scard->irp_list); + + xfree(dev); + return; +} + + +static void +scard_process_irp(SCARD_DEVICE* scard, IRP* irp) +{ + switch (irp->MajorFunction) + { + case IRP_MJ_DEVICE_CONTROL: + scard_device_control(scard, irp); + break; + + default: + printf("MajorFunction 0x%X unexpected for smartcards.", irp->MajorFunction); + DEBUG_WARN("Smartcard MajorFunction 0x%X not supported.", irp->MajorFunction); + irp->IoStatus = STATUS_NOT_SUPPORTED; + irp->Complete(irp); + break; + } +} + + +static void +scard_process_irp_list(SCARD_DEVICE* scard) +{ + IRP *irp; + + while (!freerdp_thread_is_stopped(scard->thread)) + { + freerdp_thread_lock(scard->thread); + irp = (IRP *) list_dequeue(scard->irp_list); + freerdp_thread_unlock(scard->thread); + + if (irp == NULL) + break; + + scard_process_irp(scard, irp); + } +} + + +struct scard_irp_thread_args { + SCARD_DEVICE* scard; + IRP* irp; + freerdp_thread* thread; +}; + + +static void +scard_process_irp_thread_func(struct scard_irp_thread_args* args) +{ + scard_process_irp(args->scard, args->irp); + + freerdp_thread_free(args->thread); + xfree(args); +} + + +static void * +scard_thread_func(void* arg) +{ + SCARD_DEVICE* scard = (SCARD_DEVICE*) arg; + + while (1) + { + freerdp_thread_wait(scard->thread); + + if (freerdp_thread_is_stopped(scard->thread)) + break; + + freerdp_thread_reset(scard->thread); + scard_process_irp_list(scard); + } + + freerdp_thread_quit(scard->thread); + + return NULL; +} + + +static void +scard_irp_request(DEVICE* device, IRP* irp) +{ + SCARD_DEVICE* scard = (SCARD_DEVICE*)device; + + if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL && + scard_async_op(irp)) + { + /* + * certain potentially long running operations + * get their own thread + * TODO: revise this mechanism.. maybe worker pool + */ + struct scard_irp_thread_args *args = xmalloc(sizeof(struct scard_irp_thread_args)); + + + args->thread = freerdp_thread_new(); + args->scard = scard; + args->irp = irp; + freerdp_thread_start(args->thread, scard_process_irp_thread_func, args); + + return; + } + + freerdp_thread_lock(scard->thread); + list_enqueue(scard->irp_list, irp); + freerdp_thread_unlock(scard->thread); + + freerdp_thread_signal(scard->thread); +} + + +int +DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints) +{ + SCARD_DEVICE* scard; + char* name; + char* path; + int i, length; + + name = (char *)pEntryPoints->plugin_data->data[1]; + path = (char *)pEntryPoints->plugin_data->data[2]; + + if (name) + { + /* TODO: check if server supports sc redirect (version 5.1) */ + + scard = xnew(SCARD_DEVICE); + + scard->device.type = RDPDR_DTYP_SMARTCARD; + scard->device.name = "SCARD"; + scard->device.IRPRequest = scard_irp_request; + scard->device.Free = scard_free; + + length = strlen(scard->device.name); + scard->device.data = stream_new(length + 1); + + for (i = 0; i <= length; i++) + stream_write_uint8(scard->device.data, name[i] < 0 ? '_' : name[i]); + + scard->path = path; + + scard->irp_list = list_new(); + scard->thread = freerdp_thread_new(); + + pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE *)scard); + + freerdp_thread_start(scard->thread, scard_thread_func, scard); + } + + return 0; +} diff --git a/channels/rdpdr/smartcard/scard_main.h b/channels/rdpdr/smartcard/scard_main.h new file mode 100644 index 000000000..7813f486c --- /dev/null +++ b/channels/rdpdr/smartcard/scard_main.h @@ -0,0 +1,52 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Redirected Smart Card Device Service + + Copyright 2011 O.S. Systems Software Ltda. + Copyright 2011 Eduardo Fiss Beloni + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef __SCARD_MAIN_H +#define __SCARD_MAIN_H + +#include + +#include "devman.h" +#include "rdpdr_types.h" +#include + +struct _SCARD_DEVICE +{ + DEVICE device; + + char * name; + char * path; + + LIST* irp_list; + + freerdp_thread* thread; +}; +typedef struct _SCARD_DEVICE SCARD_DEVICE; + +#ifdef WITH_DEBUG_SCARD +#define DEBUG_SCARD(fmt, ...) DEBUG_CLASS(SCARD, fmt, ## __VA_ARGS__) +#else +#define DEBUG_SCARD(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + +boolean scard_async_op(IRP*); +void scard_device_control(SCARD_DEVICE*, IRP*); + +#endif diff --git a/channels/rdpdr/smartcard/scard_operations.c b/channels/rdpdr/smartcard/scard_operations.c new file mode 100644 index 000000000..7bce705d5 --- /dev/null +++ b/channels/rdpdr/smartcard/scard_operations.c @@ -0,0 +1,1547 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Redirected Smart Card Device Service + + Copyright (C) Alexi Volkov 2006 + Copyright 2011 O.S. Systems Software Ltda. + Copyright 2011 Anthony Tong + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rdpdr_types.h" +#include "rdpdr_constants.h" + +#include "scard_main.h" + + +/* [MS-RDPESC] 3.1.4 */ +#define SCARD_IOCTL_ESTABLISH_CONTEXT 0x00090014 /* EstablishContext */ +#define SCARD_IOCTL_RELEASE_CONTEXT 0x00090018 /* ReleaseContext */ +#define SCARD_IOCTL_IS_VALID_CONTEXT 0x0009001C /* IsValidContext */ +#define SCARD_IOCTL_LIST_READER_GROUPS 0x00090020 /* ListReaderGroups */ +#define SCARD_IOCTL_LIST_READERS 0x00090028 /* ListReadersA */ +#define SCARD_IOCTL_INTRODUCE_READER_GROUP 0x00090050 /* IntroduceReaderGroup */ +#define SCARD_IOCTL_FORGET_READER_GROUP 0x00090058 /* ForgetReader */ +#define SCARD_IOCTL_INTRODUCE_READER 0x00090060 /* IntroduceReader */ +#define SCARD_IOCTL_FORGET_READER 0x00090068 /* IntroduceReader */ +#define SCARD_IOCTL_ADD_READER_TO_GROUP 0x00090070 /* AddReaderToGroup */ +#define SCARD_IOCTL_REMOVE_READER_FROM_GROUP 0x00090078 /* RemoveReaderFromGroup */ +#define SCARD_IOCTL_GET_STATUS_CHANGE 0x000900A0 /* GetStatusChangeA */ +#define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */ +#define SCARD_IOCTL_CONNECT 0x000900AC /* ConnectA */ +#define SCARD_IOCTL_RECONNECT 0x000900B4 /* Reconnect */ +#define SCARD_IOCTL_DISCONNECT 0x000900B8 /* Disconnect */ +#define SCARD_IOCTL_BEGIN_TRANSACTION 0x000900BC /* BeginTransaction */ +#define SCARD_IOCTL_END_TRANSACTION 0x000900C0 /* EndTransaction */ +#define SCARD_IOCTL_STATE 0x000900C4 /* State */ +#define SCARD_IOCTL_STATUS 0x000900C8 /* StatusA */ +#define SCARD_IOCTL_TRANSMIT 0x000900D0 /* Transmit */ +#define SCARD_IOCTL_CONTROL 0x000900D4 /* Control */ +#define SCARD_IOCTL_GETATTRIB 0x000900D8 /* GetAttrib */ +#define SCARD_IOCTL_SETATTRIB 0x000900DC /* SetAttrib */ +#define SCARD_IOCTL_ACCESS_STARTED_EVENT 0x000900E0 /* SCardAccessStartedEvent */ +#define SCARD_IOCTL_LOCATE_CARDS_BY_ATR 0x000900E8 /* LocateCardsByATR */ + + +#define SCARD_INPUT_LINKED 0xFFFFFFFF + + +static uint32 +sc_output_string(IRP* irp, char *src, boolean wide) +{ + uint8* p; + uint32 len; + + p = stream_get_tail(irp->output); + len = strlen(src) + 1; + + if (wide) + { + // len = mbstowcs(p, src, len); // need to setlocale for 2byte wchar.. + int i; + for (i = 0; i < len; i++ ) + { + p[2 * i] = src[i] < 0 ? '?' : src[i]; + p[2 * i + 1] = '\0'; + } + + len *= 2; + } + else + { + memcpy(p, src, len); + } + + stream_seek(irp->output, len); + return len; +} + + +static void +sc_output_alignment(IRP *irp, uint32 seed) +{ + uint32 size = stream_get_length(irp->output) - 20; + uint32 add = (seed - (size % seed)) % seed; + + if (add > 0) + stream_write_zero(irp->output, add); +} + + +static void +sc_output_repos(IRP* irp, uint32 written) +{ + uint32 add = (4 - (written % 4)) % 4; + + if (add > 0) + stream_write_zero(irp->output, add); +} + + +static uint32 +sc_output_return(IRP* irp, uint32 rv) +{ + stream_write_zero(irp->output, 256); + return rv; +} + + +static void +sc_output_buffer_limit(IRP* irp, char *buffer, unsigned int length, unsigned int highLimit) +{ + int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length)); + + stream_write_uint32(irp->output, header); + + if (length <= 0) + { + stream_write_uint32(irp->output, 0); + } + else + { + if (header < length) + length = header; + + stream_write(irp->output, buffer, length); + sc_output_repos(irp, length); + } +} + + +static void +sc_output_buffer(IRP* irp, char *buffer, unsigned int length) +{ + sc_output_buffer_limit(irp, buffer, length, 0x7FFFFFFF); +} + + +static void +sc_output_buffer_start_limit(IRP *irp, int length, int highLimit) +{ + int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length)); + + stream_write_uint32(irp->output, header); + stream_write_uint32(irp->output, 0x00000001); /* Magic DWORD - any non zero */ +} + + +static void +sc_output_buffer_start(IRP *irp, int length) +{ + sc_output_buffer_start_limit(irp, length, 0x7FFFFFFF); +} + + +static uint32 +sc_input_string(IRP* irp, char **dest, uint32 dataLength, boolean wide) +{ + char *buffer; + int bufferSize; + + bufferSize = wide ? (2 * dataLength) : dataLength; + buffer = xmalloc(bufferSize + 2); /* reserve 2 bytes for the '\0' */ + + stream_read(irp->input, buffer, bufferSize); + if (wide) + { + int i; + for (i = 0; i < dataLength; i++) + { + if ((buffer[2 * i] < 0) || (buffer[2 * i + 1] != 0)) + buffer[i] = '?'; + else + buffer[i] = buffer[2 * i]; + } + } + + buffer[dataLength] = '\0'; + *dest = buffer; + + return bufferSize; +} + + +static void +sc_input_repos(IRP* irp, uint32 read) +{ + uint32 add = 4 - (read % 4); + + if (add < 4 && add > 0) + stream_seek(irp->input, add); +} + + +static void +sc_input_reader_name(IRP* irp, char **dest, boolean wide) +{ + uint32 dataLength; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, dataLength); + + DEBUG_SCARD("datalength %d", dataLength); + sc_input_repos(irp, sc_input_string(irp, dest, dataLength, wide)); +} + + +static void +sc_input_skip_linked(IRP* irp) +{ + uint32 len; + stream_read_uint32(irp->input, len); + + if (len > 0) + { + stream_seek(irp->input, len); + sc_input_repos(irp, len); + } +} + + +static uint32 +sc_map_state(uint32 state) +{ + // is this mapping still needed? + if (state & SCARD_SPECIFIC) + state = 0x00000006; + else if (state & SCARD_NEGOTIABLE) + state = 0x00000005; + else if (state & SCARD_POWERED) + state = 0x00000004; + else if (state & SCARD_SWALLOWED) + state = 0x00000003; + else if (state & SCARD_PRESENT) + state = 0x00000002; + else if (state & SCARD_ABSENT) + state = 0x00000001; + else + state = 0x00000000; + + return state; +} + + +static uint32 +handle_EstablishContext(IRP* irp) +{ + uint32 len, rv; + uint32 scope; + SCARDCONTEXT hContext = -1; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, len); + if (len != 8) + return SCARD_F_INTERNAL_ERROR; + + stream_seek_uint32(irp->input); + stream_read_uint32(irp->input, scope); + + rv = SCardEstablishContext(scope, NULL, NULL, &hContext); + + stream_write_uint32(irp->output, 4); // ? + stream_write_uint32(irp->output, -1); // ? + + stream_write_uint32(irp->output, 4); + stream_write_uint32(irp->output, hContext); + + // TODO: store hContext in allowed context list + + return SCARD_S_SUCCESS; +} + + +static uint32 +handle_ReleaseContext(IRP* irp) +{ + uint32 len, rv; + SCARDCONTEXT hContext = -1; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, len); + // if (len != 16) return SCARD_F_INTERNAL_ERROR; + + stream_seek(irp->input, 0x10); + stream_read_uint32(irp->input, hContext); + + rv = SCardReleaseContext(hContext); + if (rv) + DEBUG_SCARD("%s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("success 0x%08lx", hContext); + + return rv; +} + + +static uint32 +handle_IsValidContext(IRP* irp) +{ + uint32 rv; + SCARDCONTEXT hContext; + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, hContext); + + rv = SCardIsValidContext(hContext); + + if (rv) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success context: 0x%08x", (unsigned) hContext); + + stream_write_uint32(irp->output, rv); + + return rv; +} + + +static uint32 +handle_ListReaders(IRP* irp, boolean wide) +{ + uint32 len, rv; + SCARDCONTEXT hContext; + DWORD dwReaders; + char *readerList = NULL, *walker; + int elemLength, dataLength; + int pos, poslen1, poslen2; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, len); + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, len); + + if (len != 4) + return SCARD_F_INTERNAL_ERROR; + + stream_read_uint32(irp->input, hContext); + + /* ignore rest of [MS-RDPESC] 2.2.2.4 ListReaders_Call */ + + rv = SCARD_S_SUCCESS; +#ifdef SCARD_AUTOALLOCATE + dwReaders = SCARD_AUTOALLOCATE; + rv = SCardListReaders(hContext, NULL, (LPSTR) &readerList, &dwReaders); +#else + rv = SCardListReaders(hContext, NULL, NULL, &dwReaders); + + readerList = xmalloc(dwReaders); + rv = SCardListReaders(hContext, NULL, readerList, &dwReaders); +#endif + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + return rv; + } + + DEBUG_SCARD("Success 0x%08x %d %d", (unsigned) hContext, (unsigned) cchReaders, (int) strlen(readerList)); + + poslen1 = stream_get_pos(irp->output); + stream_seek_uint32(irp->output); + + stream_write_uint32(irp->output, 0x01760650); + + poslen2 = stream_get_pos(irp->output); + stream_seek_uint32(irp->output); + + walker = readerList; + dataLength = 0; + while (1) + { + elemLength = strlen(walker); + if (elemLength == 0) + break; + + dataLength += sc_output_string(irp, walker, wide); + walker += elemLength + 1; + elemLength = strlen(walker); + } + + dataLength += sc_output_string(irp, "\0", wide); + + pos = stream_get_pos(irp->output); + + stream_set_pos(irp->output, poslen1); + stream_write_uint32(irp->output, dataLength); + stream_set_pos(irp->output, poslen2); + stream_write_uint32(irp->output, dataLength); + + stream_set_pos(irp->output, pos); + + sc_output_repos(irp, dataLength); + sc_output_alignment(irp, 8); + +#ifdef SCARD_AUTOALLOCATE + SCardFreeMemory(hContext, readerList); +#else + xfree(readerList); +#endif + + return rv; +} + + +static uint32 +handle_GetStatusChange(IRP* irp, boolean wide) +{ + LONG rv; + SCARDCONTEXT hContext; + DWORD dwTimeout = 0; + DWORD readerCount = 0; + SCARD_READERSTATE *readerStates, *cur; + int i; + + stream_seek(irp->input, 0x18); + stream_read_uint32(irp->input, dwTimeout); + stream_read_uint32(irp->input, readerCount); + + stream_seek(irp->input, 8); + + stream_read_uint32(irp->input, hContext); + + stream_seek(irp->input, 4); + + DEBUG_SCARD("context: 0x%08x, timeout: 0x%08x, count: %d", + (unsigned) hContext, (unsigned) dwTimeout, (int) readerCount); + if (readerCount > 0) + { + readerStates = xzalloc(readerCount * sizeof(SCARD_READERSTATE)); + if (!readerStates) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + + stream_seek(irp->input, 4); + + /* + * TODO: on-wire is little endian; need to either + * convert to host endian or fix the headers to + * request the order we want + */ + stream_read_uint32(irp->input, cur->dwCurrentState); + stream_read_uint32(irp->input, cur->dwEventState); + stream_read_uint32(irp->input, cur->cbAtr); + stream_read(irp->input, cur->rgbAtr, 32); + + stream_seek(irp->input, 4); + + /* reset high bytes? */ + cur->dwCurrentState &= 0x0000FFFF; + cur->dwEventState &= 0x0000FFFF; + cur->dwEventState = 0; + } + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + uint32 dataLength; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, dataLength); + sc_input_repos(irp, sc_input_string(irp, (char **) &cur->szReader, dataLength, wide)); + + DEBUG_SCARD(" \"%s\"", cur->szReader ? cur->szReader : "NULL"); + DEBUG_SCARD(" user: 0x%08x, state: 0x%08x, event: 0x%08x", + (unsigned) cur->pvUserData, (unsigned) cur->dwCurrentState, + (unsigned) cur->dwEventState); + + if (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0) + cur->dwCurrentState |= SCARD_STATE_IGNORE; + } + } + else + { + readerStates = NULL; + } + + rv = SCardGetStatusChange(hContext, (DWORD) dwTimeout, readerStates, (DWORD) readerCount); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success"); + + stream_write_uint32(irp->output, readerCount); + stream_write_uint32(irp->output, 0x00084dd8); + stream_write_uint32(irp->output, readerCount); + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + + DEBUG_SCARD(" \"%s\"", cur->szReader ? cur->szReader : "NULL"); + DEBUG_SCARD(" user: 0x%08x, state: 0x%08x, event: 0x%08x\n", + (unsigned) cur->pvUserData, (unsigned) cur->dwCurrentState, + (unsigned) cur->dwEventState); + + /* TODO: do byte conversions if necessary */ + stream_write_uint32(irp->output, cur->dwCurrentState); + stream_write_uint32(irp->output, cur->dwEventState); + stream_write_uint32(irp->output, cur->cbAtr); + stream_write(irp->output, cur->rgbAtr, 32); + + stream_write_zero(irp->output, 4); + + xfree((void *)cur->szReader); + } + + sc_output_alignment(irp, 8); + + xfree(readerStates); + return rv; +} + + +static uint32 +handle_Cancel(IRP *irp) // TESTME +{ + LONG rv; + SCARDCONTEXT hContext; + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, hContext); + + rv = SCardCancel(hContext); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)\n", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success context: 0x%08x %s\n", (unsigned) hContext, pcsc_stringify_error(rv)); + + sc_output_alignment(irp, 8); + return rv; +} + + +static uint32 +handle_Connect(IRP* irp, boolean wide) +{ + LONG rv; + SCARDCONTEXT hContext; + char *readerName = NULL; + DWORD dwShareMode = 0; + DWORD dwPreferredProtocol = 0; + DWORD dwActiveProtocol = 0; + SCARDHANDLE hCard; + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, dwShareMode); + stream_read_uint32(irp->input, dwPreferredProtocol); + + sc_input_reader_name(irp, &readerName, wide); + + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hContext); + + DEBUG_SCARD("(context: 0x%08x, share: 0x%08x, proto: 0x%08x, reader: \"%s\")", + (unsigned) hContext, (unsigned) dwShareMode, + (unsigned) dwPreferredProtocol, readerName ? readerName : "NULL"); + + rv = SCardConnect(hContext, readerName, (DWORD) dwShareMode, + (DWORD) dwPreferredProtocol, &hCard, (DWORD *) &dwActiveProtocol); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s 0x%08x", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success 0x%08x", (unsigned) hCard); + + stream_write_uint32(irp->output, 0x00000000); + stream_write_uint32(irp->output, 0x00000000); + stream_write_uint32(irp->output, 0x00000004); + stream_write_uint32(irp->output, 0x016Cff34); + stream_write_uint32(irp->output, dwActiveProtocol); + stream_write_uint32(irp->output, 0x00000004); + stream_write_uint32(irp->output, hCard); + stream_seek(irp->output, 28); + + sc_output_alignment(irp, 8); + + xfree(readerName); + return rv; +} + + +static uint32 +handle_Reconnect(IRP* irp) // TESTME +{ + LONG rv; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + DWORD dwShareMode = 0; + DWORD dwPreferredProtocol = 0; + DWORD dwInitialization = 0; + DWORD dwActiveProtocol = 0; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwShareMode); + stream_read_uint32(irp->input, dwPreferredProtocol); + stream_read_uint32(irp->input, dwInitialization); + + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hContext); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hCard); + + DEBUG_SCARD("(context: 0x%08x, hcard: 0x%08x, share: 0x%08x, proto: 0x%08x, init: 0x%08x)", + (unsigned) hContext, (unsigned) hCard, + (unsigned) dwShareMode, (unsigned) dwPreferredProtocol, (unsigned) dwInitialization); + + rv = SCardReconnect(hCard, (DWORD) dwShareMode, (DWORD) dwPreferredProtocol, + (DWORD) dwInitialization, (LPDWORD) &dwActiveProtocol); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success (proto: 0x%08x)", (unsigned) dwActiveProtocol); + + sc_output_alignment(irp, 8); + stream_write_uint32(irp->output, dwActiveProtocol); // reversed? + + return rv; +} + + +static uint32 +handle_Disconnect(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + DWORD dwDisposition = 0; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwDisposition); + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hContext); + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hCard); + + DEBUG_SCARD("(context: 0x%08x, hcard: 0x%08x, disposition: 0x%08x)", + (unsigned) hContext, (unsigned) hCard, (unsigned) dwDisposition); + + rv = SCardDisconnect(hCard, (DWORD) dwDisposition); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success"); + + sc_output_alignment(irp, 8); + + return rv; +} + + +static uint32 +handle_BeginTransaction(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hCard; + + stream_seek(irp->input, 0x30); + stream_read_uint32(irp->input, hCard); + + rv = SCardBeginTransaction(hCard); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success hcard: 0x%08x", (unsigned) hCard); + + sc_output_alignment(irp, 8); + + return rv; +} + + +static uint32 +handle_EndTransaction(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hCard; + DWORD dwDisposition = 0; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwDisposition); + + stream_seek(irp->input, 0x0C); + stream_read_uint32(irp->input, hCard); + + rv = SCardEndTransaction(hCard, dwDisposition); + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success hcard: 0x%08x", (unsigned) hCard); + + sc_output_alignment(irp, 8); + return rv; +} + + +static uint32 +handle_State(IRP* irp) // TESTME +{ + LONG rv; + SCARDHANDLE hCard; + DWORD state = 0, protocol = 0; + DWORD readerLen; + DWORD atrLen = MAX_ATR_SIZE; + char * readerName; + BYTE pbAtr[MAX_ATR_SIZE]; + +#ifdef WITH_DEBUG_SCARD + int i; +#endif + + stream_seek(irp->input, 0x24); + stream_seek_uint32(irp->input); // atrLen + + stream_seek(irp->input, 0x0c); + stream_read_uint32(irp->input, hCard); + stream_seek(irp->input, 0x04); + +#ifdef SCARD_AUTOALLOCATE + readerLen = SCARD_AUTOALLOCATE; + + rv = SCardStatus(hCard, (LPSTR) &readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#else + readerLen = 256; + readerName = xmalloc(readerLen); + + rv = SCardStatus(hCard, (LPSTR) readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#endif + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + return sc_output_return(irp, rv); + } + + DEBUG_SCARD("Success (hcard: 0x%08x len: %d state: 0x%08x, proto: 0x%08x)", + (unsigned) hCard, (int) atrLen, (unsigned) state, (unsigned) protocol); + +#ifdef WITH_DEBUG_SCARD + printf(" ATR: "); + for (i = 0; i < atrLen; i++) + printf("%02x%c", pbAtr[i], (i == atrLen - 1) ? ' ' : ':'); + printf("\n"); +#endif + + state = sc_map_state(state); + + stream_write_uint32(irp->output, state); + stream_write_uint32(irp->output, protocol); + stream_write_uint32(irp->output, atrLen); + stream_write_uint32(irp->output, 0x00000001); + stream_write_uint32(irp->output, atrLen); + stream_write(irp->output, pbAtr, atrLen); + + sc_output_repos(irp, atrLen); + sc_output_alignment(irp, 8); + +#ifdef SCARD_AUTOALLOCATE + SCard +#else + xfree(readerName); +#endif + + return rv; +} + + +static DWORD +handle_Status(IRP *irp, boolean wide) +{ + LONG rv; + SCARDHANDLE hCard; + DWORD state, protocol; + DWORD readerLen = 0; + DWORD atrLen = 0; + char * readerName; + BYTE pbAtr[MAX_ATR_SIZE]; + uint32 dataLength; + int pos, poslen1, poslen2; + +#ifdef WITH_DEBUG_SCARD + int i; +#endif + + stream_seek(irp->input, 0x24); + stream_read_uint32(irp->input, readerLen); + stream_read_uint32(irp->input, atrLen); + stream_seek(irp->input, 0x0c); + stream_read_uint32(irp->input, hCard); + stream_seek(irp->input, 0x4); + + atrLen = MAX_ATR_SIZE; + +#ifdef SCARD_AUTOALLOCATE + readerLen = SCARD_AUTOALLOCATE; + + rv = SCardStatus(hCard, (LPSTR) &readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#else + readerLen = 256; + readerName = xmalloc(readerLen); + + rv = SCardStatus(hCard, (LPSTR) readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#endif + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + return sc_output_return(irp, rv); + } + + DEBUG_SCARD("Success (state: 0x%08x, proto: 0x%08x)", (unsigned) state, (unsigned) protocol); + DEBUG_SCARD(" Reader: \"%s\"", readerName ? readerName : "NULL"); + +#ifdef WITH_DEBUG_SCARD + printf(" ATR: "); + for (i = 0; i < atrLen; i++) + printf("%02x%c", pbAtr[i], (i == atrLen - 1) ? ' ' : ':'); + printf("\n"); +#endif + + state = sc_map_state(state); + + poslen1 = stream_get_pos(irp->output); + stream_write_uint32(irp->output, readerLen); + stream_write_uint32(irp->output, 0x00020000); + stream_write_uint32(irp->output, state); + stream_write_uint32(irp->output, protocol); + stream_write(irp->output, pbAtr, atrLen); + + if (atrLen < 32) + stream_write_zero(irp->output, 32 - atrLen); + stream_write_uint32(irp->output, atrLen); + + poslen2 = stream_get_pos(irp->output); + stream_write_uint32(irp->output, readerLen); + + dataLength = sc_output_string(irp, readerName, wide); + dataLength += sc_output_string(irp, "\0", wide); + sc_output_repos(irp, dataLength); + + pos = stream_get_pos(irp->output); + stream_set_pos(irp->output, poslen1); + stream_write_uint32(irp->output,dataLength); + stream_set_pos(irp->output, poslen2); + stream_write_uint32(irp->output,dataLength); + stream_set_pos(irp->output, pos); + + sc_output_alignment(irp, 8); + +#ifdef SCARD_AUTOALLOCATE + //SCardFreeMemory(NULL, readerName); + free(readerName); +#else + xfree(readerName); +#endif + + return rv; +} + + +static uint32 +handle_Transmit(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hCard; + uint32 map[7], linkedLen; + SCARD_IO_REQUEST pioSendPci, pioRecvPci, *pPioRecvPci; + DWORD cbSendLength = 0, cbRecvLength = 0; + BYTE *sendBuf = NULL, *recvBuf = NULL; + + stream_seek(irp->input, 0x14); + stream_read_uint32(irp->input, map[0]); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, map[1]); + + stream_read_uint32(irp->input, pioSendPci.dwProtocol); + stream_read_uint32(irp->input, pioSendPci.cbPciLength); + + stream_read_uint32(irp->input, map[2]); + stream_read_uint32(irp->input, cbSendLength); + stream_read_uint32(irp->input, map[3]); + stream_read_uint32(irp->input, map[4]); + stream_read_uint32(irp->input, map[5]); + stream_read_uint32(irp->input, cbRecvLength); + + if (map[0] & SCARD_INPUT_LINKED) + sc_input_skip_linked(irp); + + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hCard); + + if (map[2] & SCARD_INPUT_LINKED) + { + /* sendPci */ + stream_read_uint32(irp->input, linkedLen); + + stream_read_uint32(irp->input, pioSendPci.dwProtocol); + stream_seek(irp->input, linkedLen - 4); + + sc_input_repos(irp, linkedLen); + } + pioSendPci.cbPciLength = sizeof(SCARD_IO_REQUEST); + + if (map[3] & SCARD_INPUT_LINKED) + { + /* send buffer */ + stream_read_uint32(irp->input, linkedLen); + + sendBuf = xmalloc(linkedLen); + stream_read(irp->input, sendBuf, linkedLen); + sc_input_repos(irp, linkedLen); + } + + if (cbRecvLength) + recvBuf = xmalloc(cbRecvLength); + + if (map[4] & SCARD_INPUT_LINKED) + { + /* recvPci */ + stream_read_uint32(irp->input, linkedLen); + + stream_read_uint32(irp->input, pioRecvPci.dwProtocol); + stream_seek(irp->input, linkedLen - 4); + + sc_input_repos(irp, linkedLen); + + stream_read_uint32(irp->input, map[6]); + if (map[6] & SCARD_INPUT_LINKED) + { + // not sure what this is + stream_read_uint32(irp->input, linkedLen); + stream_seek(irp->input, linkedLen); + + sc_input_repos(irp, linkedLen); + } + pioRecvPci.cbPciLength = sizeof(SCARD_IO_REQUEST); + pPioRecvPci = &pioRecvPci; + } + else + { + pPioRecvPci = NULL; + } + pPioRecvPci = NULL; + + DEBUG_SCARD("SCardTransmit(hcard: 0x%08lx, send: %d bytes, recv: %d bytes)", + (long unsigned) hCard, (int) cbSendLength, (int) cbRecvLength); + + rv = SCardTransmit(hCard, &pioSendPci, sendBuf, cbSendLength, + pPioRecvPci, recvBuf, &cbRecvLength); + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + } + else + { + DEBUG_SCARD("Success (%d bytes)", (int) cbRecvLength); + + stream_write_uint32(irp->output, 0); /* pioRecvPci 0x00; */ + + sc_output_buffer_start(irp, cbRecvLength); /* start of recvBuf output */ + + sc_output_buffer(irp, (char *) recvBuf, cbRecvLength); + } + + sc_output_alignment(irp, 8); + + xfree(sendBuf); + xfree(recvBuf); + + return rv; +} + + +static uint32 +handle_Control(IRP* irp) // TESTME +{ + LONG rv; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + uint32 map[3]; + uint32 controlCode; + BYTE *recvBuffer = NULL, *sendBuffer = NULL; + uint32 recvLength; + DWORD nBytesReturned; + DWORD outBufferSize; + + stream_seek(irp->input, 0x14); + stream_read_uint32(irp->input, map[0]); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, map[1]); + stream_read_uint32(irp->input, controlCode); + stream_read_uint32(irp->input, recvLength); + stream_read_uint32(irp->input, map[2]); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, outBufferSize); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hContext); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hCard); + + if (map[2] & SCARD_INPUT_LINKED) + { + /* read real input size */ + stream_read_uint32(irp->input, recvLength); + + recvBuffer = xmalloc(recvLength); + if (!recvBuffer) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + stream_read(irp->input, recvBuffer, recvLength); + } + + nBytesReturned = outBufferSize; + sendBuffer = xmalloc(outBufferSize); + if (!sendBuffer) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + rv = SCardControl(hCard, (DWORD) controlCode, recvBuffer, (DWORD) recvLength, + sendBuffer, (DWORD) outBufferSize, &nBytesReturned); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success (out: %u bytes)", (unsigned) nBytesReturned); + + stream_write_uint32(irp->output, (uint32) nBytesReturned); + stream_write_uint32(irp->output, 0x00000004); + stream_write_uint32(irp->output, nBytesReturned); + + if (nBytesReturned > 0) + { + stream_write(irp->output, sendBuffer, nBytesReturned); + sc_output_repos(irp, nBytesReturned); + } + + sc_output_alignment(irp, 8); + + xfree(recvBuffer); + xfree(sendBuffer); + + return rv; +} + + +static uint32 +handle_GetAttrib(IRP* irp) +{ + LONG rv; + SCARDHANDLE hCard; + DWORD dwAttrId = 0, dwAttrLen = 0; + DWORD attrLen = 0; + unsigned char *pbAttr; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwAttrId); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, dwAttrLen); + stream_seek(irp->input, 0xc); + stream_read_uint32(irp->input, hCard); + + DEBUG_SCARD("hcard: 0x%08x, attrib: 0x%08x (%d bytes)\n", + (unsigned) hCard, (unsigned) dwAttrId, (int) dwAttrLen); + +#ifdef SCARD_AUTOALLOCATE + if(dwAttrLen == 0) + { + attrLen = 0; + } + else + { + attrLen = SCARD_AUTOALLOCATE; + } +#endif + + rv = SCardGetAttrib(hCard, dwAttrId, attrLen == 0 ? NULL : (unsigned char *)&pbAttr, &attrLen); + + if(dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_A && rv == SCARD_E_UNSUPPORTED_FEATURE) + { + rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_W, + attrLen == 0 ? NULL : (unsigned char *)&pbAttr, &attrLen); + } + if(dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_W && rv == SCARD_E_UNSUPPORTED_FEATURE) + { + rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_A, + attrLen == 0 ? NULL : (unsigned char *)&pbAttr, &attrLen); + } + if(attrLen > dwAttrLen && pbAttr != NULL) + { + rv = SCARD_E_INSUFFICIENT_BUFFER; + } + dwAttrLen = attrLen; + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); + free(pbAttr); + return sc_output_return(irp, rv); + } + else + { + DEBUG_SCARD("Success (%d bytes)", (int) dwAttrLen); + + stream_write_uint32(irp->output, dwAttrLen); + stream_write_uint32(irp->output, 0x00000200); + stream_write_uint32(irp->output, dwAttrLen); + + if (!pbAttr) + { + stream_write_zero(irp->output, dwAttrLen); + } + else + { + stream_write(irp->output, pbAttr, dwAttrLen); + } + sc_output_repos(irp, dwAttrLen); + // Align to multiple of 4 + stream_write_uint32(irp->output, 0); + } + sc_output_alignment(irp, 8); + + xfree(pbAttr); + + return rv; +} + + +static uint32 +handle_AccessStartedEvent(IRP* irp) +{ + stream_write_zero(irp->output, 8); + return SCARD_S_SUCCESS; +} + + +void +scard_error(SCARD_DEVICE* scard, IRP* irp, uint32 ntstatus) +{ + // [MS-RDPESC] 3.1.4.4 + printf("scard processing error %x\n", ntstatus); + + stream_set_pos(irp->output, 0); // CHECKME + irp->IoStatus = ntstatus; + irp->Complete(irp); +} + + +/* http://msdn.microsoft.com/en-gb/library/ms938473.aspx */ +typedef struct _SERVER_SCARD_ATRMASK +{ + uint32 cbAtr; + unsigned char rgbAtr[36]; + unsigned char rgbMask[36]; +} +SERVER_SCARD_ATRMASK; + +static uint32 +handle_LocateCardsByATR(IRP* irp, boolean wide) // TESTME +{ + int i, j, k; + LONG rv; + SCARDCONTEXT hContext; + SERVER_SCARD_ATRMASK *pAtrMasks, *curAtr; + uint32 atrMaskCount = 0; + uint32 readerCount = 0; + SCARD_READERSTATE *readerStates, *rsCur, *cur; + + stream_seek(irp->input, 0x2c); + stream_read_uint32(irp->input, hContext); + stream_read_uint32(irp->input, atrMaskCount); + + pAtrMasks = xmalloc(atrMaskCount * sizeof(SERVER_SCARD_ATRMASK)); + if (!pAtrMasks) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + for (i = 0; i < atrMaskCount; i++) + { + stream_read_uint32(irp->input, pAtrMasks[i].cbAtr); + stream_read(irp->input, pAtrMasks[i].rgbAtr, 36); + stream_read(irp->input, pAtrMasks[i].rgbMask, 36); + } + + stream_read_uint32(irp->input, readerCount); + + readerStates = xzalloc(readerCount * sizeof(SCARD_READERSTATE)); + if (!readerStates) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + + stream_seek(irp->input, 4); + + /* + * TODO: on-wire is little endian; need to either + * convert to host endian or fix the headers to + * request the order we want + */ + stream_read_uint32(irp->input, cur->dwCurrentState); + stream_read_uint32(irp->input, cur->dwEventState); + stream_read_uint32(irp->input, cur->cbAtr); + stream_read(irp->input, cur->rgbAtr, 32); + + stream_seek(irp->input, 4); + + /* reset high bytes? */ + cur->dwCurrentState &= 0x0000FFFF; + cur->dwEventState &= 0x0000FFFF; + cur->dwEventState = 0; + } + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + uint32 dataLength; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, dataLength); + sc_input_repos(irp, sc_input_string(irp, (char **) &cur->szReader, dataLength, wide)); + + DEBUG_SCARD(" \"%s\"", cur->szReader ? cur->szReader : "NULL"); + DEBUG_SCARD(" user: 0x%08x, state: 0x%08x, event: 0x%08x", + (unsigned) cur->pvUserData, (unsigned) cur->dwCurrentState, + (unsigned) cur->dwEventState); + + if (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0) + cur->dwCurrentState |= SCARD_STATE_IGNORE; + } + + rv = SCardGetStatusChange(hContext, 0x00000001, readerStates, readerCount); + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", + pcsc_stringify_error(rv), (unsigned) rv); + + return sc_output_return(irp, rv); + } + + DEBUG_SCARD("Success"); + for (i = 0, curAtr = pAtrMasks; i < atrMaskCount; i++, curAtr++) + { + for (j = 0, rsCur = readerStates; j < readerCount; j++, rsCur++) + { + boolean equal = 1; + for (k = 0; k < cur->cbAtr; k++) + { + if ((curAtr->rgbAtr[k] & curAtr->rgbMask[k]) != + (rsCur->rgbAtr[k] & curAtr->rgbMask[k])) + { + equal = 0; + break; + } + } + if (equal) + { + rsCur->dwEventState |= 0x00000040; /* SCARD_STATE_ATRMATCH 0x00000040 */ + } + } + } + + stream_write_uint32(irp->output, readerCount); + stream_write_uint32(irp->output, 0x00084dd8); + stream_write_uint32(irp->output, readerCount); + + for (i = 0, rsCur = readerStates; i < readerCount; i++, rsCur++) + { + stream_write_uint32(irp->output, cur->dwCurrentState); + stream_write_uint32(irp->output, cur->dwEventState); + stream_write_uint32(irp->output, cur->cbAtr); + stream_write(irp->output, cur->rgbAtr, 32); + + stream_write_zero(irp->output, 4); + + xfree((void *)cur->szReader); + } + + sc_output_alignment(irp, 8); + + free(readerStates); + + return rv; +} + + +boolean +scard_async_op(IRP* irp) +{ + uint32 ioctl_code; + + /* peek ahead */ + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, ioctl_code); + stream_rewind(irp->input, 12); + + switch (ioctl_code) + { + /* non-blocking events */ + case SCARD_IOCTL_ACCESS_STARTED_EVENT: + + case SCARD_IOCTL_ESTABLISH_CONTEXT: + case SCARD_IOCTL_RELEASE_CONTEXT: + case SCARD_IOCTL_IS_VALID_CONTEXT: + + return False; + break; + + /* async events */ + case SCARD_IOCTL_GET_STATUS_CHANGE: + case SCARD_IOCTL_GET_STATUS_CHANGE + 4: + + case SCARD_IOCTL_TRANSMIT: + + case SCARD_IOCTL_STATUS: + case SCARD_IOCTL_STATUS + 4: + return True; + break; + + default: + break; + } + + /* default to async */ + return True; +} + + +void +scard_device_control(SCARD_DEVICE* scard, IRP* irp) +{ + uint32 output_len, input_len, ioctl_code; + uint32 stream_len, result; + uint32 pos, pad_len; + uint32 irp_len; + uint32 irp_result_pos, output_len_pos, result_pos; + + stream_read_uint32(irp->input, output_len); + stream_read_uint32(irp->input, input_len); + stream_read_uint32(irp->input, ioctl_code); + + stream_seek(irp->input, 20); /* padding */ + + // stream_seek(irp->input, 4); /* TODO: parse len, le, v1 */ + // stream_seek(irp->input, 4); /* 0xcccccccc */ + // stream_seek(irp->input, 4); /* rpce len */ + + /* [MS-RDPESC] 3.2.5.1 Sending Outgoing Messages */ + stream_extend(irp->output, 2048); + + irp_result_pos = stream_get_pos(irp->output); + + stream_write_uint32(irp->output, 0x00081001); /* len 8, LE, v1 */ + + /* [MS-RPCE] 2.2.6.1 */ + stream_write_uint32(irp->output, 0x00081001); /* len 8, LE, v1 */ + stream_write_uint32(irp->output, 0xcccccccc); /* filler */ + + output_len_pos = stream_get_pos(irp->output); + stream_seek(irp->output, 4); /* size */ + + stream_write_uint32(irp->output, 0x0); /* filler */ + + result_pos = stream_get_pos(irp->output); + stream_seek(irp->output, 4); /* result */ + + /* body */ + switch (ioctl_code) + { + case SCARD_IOCTL_ESTABLISH_CONTEXT: + result = handle_EstablishContext(irp); + break; + + case SCARD_IOCTL_IS_VALID_CONTEXT: + result = handle_IsValidContext(irp); + break; + + case SCARD_IOCTL_RELEASE_CONTEXT: + result = handle_ReleaseContext(irp); + break; + + case SCARD_IOCTL_LIST_READERS: + result = handle_ListReaders(irp, 0); + break; + case SCARD_IOCTL_LIST_READERS + 4: + result = handle_ListReaders(irp, 1); + break; + + case SCARD_IOCTL_LIST_READER_GROUPS: + case SCARD_IOCTL_LIST_READER_GROUPS + 4: + // typically not used unless list_readers fail + result = SCARD_F_INTERNAL_ERROR; + break; + + case SCARD_IOCTL_GET_STATUS_CHANGE: + result = handle_GetStatusChange(irp, 0); + break; + case SCARD_IOCTL_GET_STATUS_CHANGE + 4: + result = handle_GetStatusChange(irp, 1); + break; + + case SCARD_IOCTL_CANCEL: + result = handle_Cancel(irp); + break; + + case SCARD_IOCTL_CONNECT: + result = handle_Connect(irp, 0); + break; + case SCARD_IOCTL_CONNECT + 4: + result = handle_Connect(irp, 1); + break; + + case SCARD_IOCTL_RECONNECT: + result = handle_Reconnect(irp); + break; + + case SCARD_IOCTL_DISCONNECT: + result = handle_Disconnect(irp); + break; + + case SCARD_IOCTL_BEGIN_TRANSACTION: + result = handle_BeginTransaction(irp); + break; + + case SCARD_IOCTL_END_TRANSACTION: + result = handle_EndTransaction(irp); + break; + + case SCARD_IOCTL_STATE: + result = handle_State(irp); + break; + + case SCARD_IOCTL_STATUS: + result = handle_Status(irp, 0); + break; + case SCARD_IOCTL_STATUS + 4: + result = handle_Status(irp, 1); + break; + + case SCARD_IOCTL_TRANSMIT: + result = handle_Transmit(irp); + break; + + case SCARD_IOCTL_CONTROL: + result = handle_Control(irp); + break; + + case SCARD_IOCTL_GETATTRIB: + result = handle_GetAttrib(irp); + break; + + case SCARD_IOCTL_ACCESS_STARTED_EVENT: + result = handle_AccessStartedEvent(irp); + break; + + case SCARD_IOCTL_LOCATE_CARDS_BY_ATR: + result = handle_LocateCardsByATR(irp, 0); + break; + case SCARD_IOCTL_LOCATE_CARDS_BY_ATR + 4: + result = handle_LocateCardsByATR(irp, 1); + break; + + default: + result = 0xc0000001; + printf("scard unknown ioctl 0x%x\n", ioctl_code); + break; + } + + /* look for NTSTATUS errors */ + if ((result & 0xc0000000) == 0xc0000000) + return scard_error(scard, irp, result); + + /* per Ludovic Rousseau, map different usage of this particular + * error code between pcsc-lite & windows */ + if (result == 0x8010001F) + result = 0x80100022; + + /* handle response packet */ + pos = stream_get_pos(irp->output); + stream_len = pos - irp_result_pos - 4; + + stream_set_pos(irp->output, output_len_pos); + stream_write_uint32(irp->output, stream_len - 24); + + stream_set_pos(irp->output, result_pos); + stream_write_uint32(irp->output, result); + + stream_set_pos(irp->output, pos); + + /* pad stream to 16 byte align */ + pad_len = stream_len % 16; + stream_write_zero(irp->output, pad_len); + pos = stream_get_pos(irp->output); + irp_len = stream_len + pad_len; + + stream_set_pos(irp->output, irp_result_pos); + stream_write_uint32(irp->output, irp_len); + stream_set_pos(irp->output, pos); + +#ifdef WITH_DEBUG_SCARD + freerdp_hexdump(stream_get_data(irp->output), stream_get_length(irp->output)); +#endif + irp->IoStatus = 0; + + irp->Complete(irp); + +} + diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake new file mode 100644 index 000000000..a113a0298 --- /dev/null +++ b/cmake/FindPCSC.cmake @@ -0,0 +1,3 @@ +pkg_check_modules(PCSC libpcsclite) + +# TODO: pcsc version diff --git a/config.h.in b/config.h.in index 9b7c01f26..8b4895baa 100644 --- a/config.h.in +++ b/config.h.in @@ -34,5 +34,6 @@ #cmakedefine WITH_DEBUG_X11 #cmakedefine WITH_DEBUG_RAIL #cmakedefine WITH_DEBUG_XV +#cmakedefine WITH_DEBUG_SCARD #endif