/** * xrdp: A Remote Desktop Protocol server. * * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com * Copyright (C) Jay Sorg 2013 jay.sorg@gmail.com * * 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. * */ /** * @file sesman/chansrv/smartcard.c * * smartcard redirection support * * This file implements some of the PDUs detailed in [MS-RDPESC]. * * The PDUs use DCE IDL structs. These are required to be re-interpreted * in DCE NDR (Netword Data Representation) * * For more information on this subject see DCE publication C706 * "DCE 1.1: Remote Procedure Call" 1997. In particular:- * Section 4.2 : Describes the IDL * Section 14 : Describes the NDR */ #if defined(HAVE_CONFIG_H) #include #endif #include #include "os_calls.h" #include "string_calls.h" #include "smartcard.h" #include "log.h" #include "irp.h" #include "devredir.h" #include "smartcard_pcsc.h" #include "chansrv.h" /* * TODO * * o ensure that all wide calls are handled correctly * * o need to query client for build number and determine whether we should use * SCREDIR_VERSION_XP or SCREDIR_VERSION_LONGHORN * * o need to call scard_release_resources() * * o why is win 7 sending SCARD_IOCTL_ACCESS_STARTED_EVENT first * 0000 00 01 00 00 04 00 00 00 e0 00 09 00 00 00 00 00 ................ * 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 0020 28 b7 9d 02 */ /* * Notes: * * XP and Server 2003 use version SCREDIR_VERSION_XP functions 5 - 58 * Vista and Server 2008 use version SCREDIR_VERSION_LONGHORN functions 5 - 64 * if TS Client's build number is >= 4,034 use SCREDIR_VERSION_LONGHORN */ /* [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_A 0x00090028 /* ListReaders ASCII */ #define SCARD_IOCTL_LIST_READERS_W 0x0009002C /* ListReaders Wide */ #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_A 0x000900A0 /* GetStatusChangeA */ #define SCARD_IOCTL_GET_STATUS_CHANGE_W 0x000900A4 /* GetStatusChangeW */ #define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */ #define SCARD_IOCTL_CONNECT_A 0x000900AC /* ConnectA */ #define SCARD_IOCTL_CONNECT_W 0x000900B0 /* ConnectW */ #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_A 0x000900C8 /* StatusA */ #define SCARD_IOCTL_STATUS_W 0x000900CC /* StatusW */ #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 */ /* scope used in EstablishContextCall */ #define SCARD_SCOPE_USER 0x00000000 #define SCARD_SCOPE_TERMINAL 0x00000001 #define SCARD_SCOPE_SYSTEM 0x00000002 /* disposition - action to take on card */ #define SCARD_LEAVE_CARD 0x00000000 #define SCARD_RESET_CARD 0x00000001 #define SCARD_UNPOWER_CARD 0x00000002 #define SCARD_EJECT_CARD 0x00000003 #define MAX_SMARTCARDS 16 /* stores info about a smart card */ typedef struct smartcard { tui32 DeviceId; } SMARTCARD; /* globals */ SMARTCARD *smartcards[MAX_SMARTCARDS]; int g_smartcards_inited = 0; static tui32 g_device_id = 0; static int g_scard_index = 0; /* externs */ extern tui32 g_completion_id; extern int g_rdpdr_chan_id; /* in chansrv.c */ /****************************************************************************** ** static functions local to this file ** ******************************************************************************/ static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl); static int scard_add_new_device(tui32 device_id); static int scard_get_free_slot(void); static void scard_release_resources(void); static void scard_send_EstablishContext(IRP *irp, int scope); static void scard_send_ReleaseContext(IRP *irp, char *context, int context_bytes); static void scard_send_IsContextValid(IRP *irp, char *context, int context_bytes); static void scard_send_ListReaders(IRP *irp, char *context, int context_bytes, char *groups, int cchReaders, int wide); static void scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes, int wide, tui32 timeout, tui32 num_readers, READER_STATE *rsa); static void scard_send_Connect(IRP *irp, char *context, int context_bytes, int wide, READER_STATE *rs); static void scard_send_Reconnect(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, READER_STATE *rs); static void scard_send_BeginTransaction(IRP *irp, char *context, int context_bytes, char *card, int card_bytes); static void scard_send_EndTransaction(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, tui32 dwDisposition); static void scard_send_Status(IRP *irp, int wide, char *context, int context_bytes, char *card, int card_bytes, int cchReaderLen, int cbAtrLen); static void scard_send_Disconnect(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, int dwDisposition); static int scard_send_Transmit(IRP *irp, char *context, int context_byte, char *card, int card_bytes, char *send_data, int send_bytes, int recv_bytes, struct xrdp_scard_io_request *send_ior, struct xrdp_scard_io_request *recv_ior); static int scard_send_Control(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, char *send_data, int send_bytes, int recv_bytes, int control_code); static int scard_send_Cancel(IRP *irp, char *context, int context_bytes); static int scard_send_GetAttrib(IRP *irp, char *card, int card_bytes, READER_STATE *rs); /****************************************************************************** ** local callbacks into this module ** ******************************************************************************/ static void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_ReleaseContext_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_IsContextValid_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_ListReaders_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_GetStatusChange_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Connect_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Reconnect_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_BeginTransaction_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_EndTransaction_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Status_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Disconnect_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Transmit_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Control_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_Cancel_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); static void scard_handle_GetAttrib_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus); /****************************************************************************** ** ** ** externally accessible functions, defined in smartcard.h ** ** ** ******************************************************************************/ /** *****************************************************************************/ void scard_device_announce(tui32 device_id) { LOG_DEVEL(LOG_LEVEL_DEBUG, "entered: device_id=%d", device_id); if (g_smartcards_inited) { LOG_DEVEL(LOG_LEVEL_ERROR, "already init"); return; } g_memset(&smartcards, 0, sizeof(smartcards)); g_smartcards_inited = 1; g_device_id = device_id; g_scard_index = scard_add_new_device(device_id); if (g_scard_index < 0) { LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_add_new_device failed with DeviceId=%d", g_device_id); } else { LOG_DEVEL(LOG_LEVEL_DEBUG, "added smartcard with DeviceId=%d to list", g_device_id); } } /** * *****************************************************************************/ int scard_get_wait_objs(tbus *objs, int *count, int *timeout) { return scard_pcsc_get_wait_objs(objs, count, timeout); } /** * *****************************************************************************/ int scard_check_wait_objs(void) { return scard_pcsc_check_wait_objs(); } /** * *****************************************************************************/ int scard_init(void) { LOG_DEVEL(LOG_LEVEL_INFO, "scard_init:"); return scard_pcsc_init(); } /** * *****************************************************************************/ int scard_deinit(void) { LOG_DEVEL(LOG_LEVEL_INFO, "scard_deinit:"); scard_pcsc_deinit(); scard_release_resources(); g_smartcards_inited = 0; return 0; } /** * *****************************************************************************/ int scard_send_establish_context(void *user_data, int scope) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_EstablishContext_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_EstablishContext(irp, scope); return 0; } /** * Release a previously established Smart Card context *****************************************************************************/ int scard_send_release_context(void *user_data, char *context, int context_bytes) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_ReleaseContext_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_ReleaseContext(irp, context, context_bytes); return 0; } /** * Checks if a previously established context is still valid *****************************************************************************/ int scard_send_is_valid_context(void *user_data, char *context, int context_bytes) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_IsContextValid_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_IsContextValid(irp, context, context_bytes); return 0; } /** * *****************************************************************************/ int scard_send_list_readers(void *user_data, char *context, int context_bytes, char *groups, int cchReaders, int wide) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_ListReaders_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_ListReaders(irp, context, context_bytes, groups, cchReaders, wide); return 0; } /** * Send get change in status command * * @param con connection to client * @param wide TRUE if unicode string * @param timeout timeout in milliseconds, -1 for infinity * @param num_readers number of entries in rsa * @param rsa array of READER_STATEs *****************************************************************************/ int scard_send_get_status_change(void *user_data, char *context, int context_bytes, int wide, tui32 timeout, tui32 num_readers, READER_STATE *rsa) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_GetStatusChange_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_GetStatusChange(irp, context, context_bytes, wide, timeout, num_readers, rsa); return 0; } /** * Open a connection to the smart card located in the reader * * @param con connection to client * @param wide TRUE if unicode string *****************************************************************************/ int scard_send_connect(void *user_data, char *context, int context_bytes, int wide, READER_STATE *rs) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Connect_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Connect(irp, context, context_bytes, wide, rs); return 0; } /** * The reconnect method re-establishes a smart card reader handle. On success, * the handle is valid once again. * * @param con connection to client * @param sc_handle handle to device * @param rs reader state where following fields are set * rs.shared_mode_flag * rs.preferred_protocol * rs.init_type *****************************************************************************/ int scard_send_reconnect(void *user_data, char *context, int context_bytes, char *card, int card_bytes, READER_STATE *rs) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Reconnect_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Reconnect(irp, context, context_bytes, card, card_bytes, rs); return 0; } /** * Lock smart card reader for exclusive access for specified smart * card reader handle. * * @param con connection to client *****************************************************************************/ int scard_send_begin_transaction(void *user_data, char *context, int context_bytes, char *card, int card_bytes) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_BeginTransaction_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_BeginTransaction(irp, context, context_bytes, card, card_bytes); return 0; } /** * Release a smart card reader after being locked by a previously * successful call to Begin Transaction * * @param con connection to client * @param sc_handle handle to smartcard *****************************************************************************/ int scard_send_end_transaction(void *user_data, char *context, int context_bytes, char *card, int card_bytes, tui32 dwDisposition) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_EndTransaction_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_EndTransaction(irp, context, context_bytes, card, card_bytes, dwDisposition); return 0; } /** * Get the status of a connection for a valid smart card reader handle * * @param con connection to client * @param wide TRUE if unicode string *****************************************************************************/ int scard_send_status(void *user_data, int wide, char *context, int context_bytes, char *card, int card_bytes, int cchReaderLen, int cbAtrLen) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Status_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Status(irp, wide, context, context_bytes, card, card_bytes, cchReaderLen, cbAtrLen); return 0; } /** * Release a smart card reader handle that was acquired in ConnectA/ConnectW * * @param con connection to client * @param sc_handle handle to smartcard *****************************************************************************/ int scard_send_disconnect(void *user_data, char *context, int context_bytes, char *card, int card_bytes, int dwDisposition) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Disconnect_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Disconnect(irp, context, context_bytes, card, card_bytes, dwDisposition); return 0; } /** * The Transmit_Call structure is used to send data to the smart card * associated with a valid context. *****************************************************************************/ int scard_send_transmit(void *user_data, char *context, int context_bytes, char *card, int card_bytes, char *send_data, int send_bytes, int recv_bytes, struct xrdp_scard_io_request *send_ior, struct xrdp_scard_io_request *recv_ior) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Transmit_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Transmit(irp, context, context_bytes, card, card_bytes, send_data, send_bytes, recv_bytes, send_ior, recv_ior); return 0; } /** * Communicate directly with the smart card reader *****************************************************************************/ int scard_send_control(void *user_data, char *context, int context_bytes, char *card, int card_bytes, char *send_data, int send_bytes, int recv_bytes, int control_code) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Control_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Control(irp, context, context_bytes, card, card_bytes, send_data, send_bytes, recv_bytes, control_code); return 0; } /** * Cancel any outstanding calls *****************************************************************************/ int scard_send_cancel(void *user_data, char *context, int context_bytes) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_Cancel_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_Cancel(irp, context, context_bytes); return 0; } /** * Get reader attributes *****************************************************************************/ int scard_send_get_attrib(void *user_data, char *card, int card_bytes, READER_STATE *rs) { IRP *irp; /* setup up IRP */ if ((irp = devredir_irp_new()) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return 1; } irp->scard_index = g_scard_index; irp->CompletionId = g_completion_id++; irp->DeviceId = g_device_id; irp->callback = scard_handle_GetAttrib_Return; irp->user_data = user_data; /* send IRP to client */ scard_send_GetAttrib(irp, card, card_bytes, rs); return 0; } /****************************************************************************** ** ** ** static functions local to this file ** ** ** ******************************************************************************/ /** * Create a new stream and insert specified IOCTL * * @param irp information about the I/O * @param ioctl the IOCTL code * * @return stream with IOCTL inserted in it, NULL on error *****************************************************************************/ static struct stream * scard_make_new_ioctl(IRP *irp, tui32 ioctl) { /* * format of device control request * * DeviceIoRequest * u16 RDPDR_CTYP_CORE * u16 PAKID_CORE_DEVICE_IOREQUEST * u32 DeviceId * u32 FileId * u32 CompletionId * u32 MajorFunction * u32 MinorFunction * * u32 OutputBufferLength SHOULD be 2048 * u32 InputBufferLength * u32 IoControlCode * 20 bytes padding * xx bytes InputBuffer (variable) */ struct stream *s; xstream_new(s, 1024 * 4); devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, irp->CompletionId, IRP_MJ_DEVICE_CONTROL, IRP_MN_NONE); xstream_wr_u32_le(s, 2048); /* OutputBufferLength */ s_push_layer(s, iso_hdr, 4); /* InputBufferLength - insert later */ xstream_wr_u32_le(s, ioctl); /* Ioctl Code */ out_uint8s(s, 20); /* padding */ /* [MS-RPCE] 2.2.6.1 */ xstream_wr_u32_le(s, 0x00081001); /* len 8, LE, v1 */ xstream_wr_u32_le(s, 0xcccccccc); /* filler */ return s; } /** * Create a new smart card device entry and insert it into smartcards[] * * @param device_id DeviceId of new card * * @return index into smartcards[] on success, -1 on failure *****************************************************************************/ static int scard_add_new_device(tui32 device_id) { int index; SMARTCARD *sc; if ((index = scard_get_free_slot()) < 0) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_get_free_slot failed"); return -1; } sc = g_new0(SMARTCARD, 1); if (sc == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory"); return -1; } sc->DeviceId = device_id; smartcards[index] = sc; return index; } /** * Find first unused entry in smartcards * * @return index of first unused entry in smartcards or -1 if smartcards * is full *****************************************************************************/ static int scard_get_free_slot(void) { int i; for (i = 0; i < MAX_SMARTCARDS; i++) { if (smartcards[i] == NULL) { LOG_DEVEL(LOG_LEVEL_DEBUG, "found free slot at index %d", i); return i; } } LOG_DEVEL(LOG_LEVEL_ERROR, "too many smart card devices; rejecting this one"); return -1; } /** * Release resources prior to shutting down *****************************************************************************/ static void scard_release_resources(void) { int i; for (i = 0; i < MAX_SMARTCARDS; i++) { if (smartcards[i] != NULL) { g_free(smartcards[i]); smartcards[i] = NULL; } } } /** * *****************************************************************************/ static void scard_send_EstablishContext(IRP *irp, int scope) { struct stream *s; int bytes; if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_ESTABLISH_CONTEXT)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, scope); out_uint32_le(s, 0x00000000); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * Release a previously established Smart Card context *****************************************************************************/ static void scard_send_ReleaseContext(IRP *irp, char *context, int context_bytes) { /* see [MS-RDPESC] 3.1.4.2 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_RELEASE_CONTEXT)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * Checks if a previously established context is still valid *****************************************************************************/ static void scard_send_IsContextValid(IRP *irp, char *context, int context_bytes) { /* see [MS-RDPESC] 3.1.4.3 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_IS_VALID_CONTEXT)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } /* * command format * * ...... * 20 bytes padding * u32 4 bytes len 8, LE, v1 * u32 4 bytes filler * 16 bytes unused (s->p currently pointed here at unused[0]) * u32 4 bytes context len * u32 4 bytes context */ s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ /* insert context */ out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /*****************************************************************************/ static void align_s(struct stream *s, unsigned int boundary) { unsigned int over = (unsigned int)(s->p - s->data) % boundary; if (over != 0) { out_uint8s(s, boundary - over); } } /** * *****************************************************************************/ static void scard_send_ListReaders(IRP *irp, char *context, int context_bytes, char *groups, int cchReaders, int wide) { /* see [MS-RDPESC] 2.2.2.4 * * IDL:- * * typedef struct _REDIR_SCARDCONTEXT { * [range(0,16)] unsigned long cbContext; * [unique] [size_is(cbContext)] byte *pbContext; * } REDIR_SCARDCONTEXT; * * struct _ListReaders_Call { * REDIR_SCARDCONTEXT Context; * [range(0, 65536)] unsigned long cBytes; * [unique] [size_is(cBytes)] const byte *mszGroups; * long fmszReadersIsNULL; * unsigned long cchReaders; * } ListReaders_Call; * * Type summary:- * * Context.cbContext Unsigned 32-bit word * Context.pbContext Embedded full pointer to conformant array of bytes * cBytes Unsigned 32-bit word * mszGroups Embedded full pointer to conformant array of bytes * fmszReaders 32-bit word * cchReaders Unsigned 32-bit word * * NDL:- * * Offset Decription * 0 Context.cbContext * 4 Referent Identifier for pbContext * 8 cBytes * 12 Referent Identifier for mszGroups (or NULL) * 16 fmszReadersIsNULL * 20 cchReaders * 24 Conformant Array pointed to by pbContext * ?? Conformant Array pointed to by mszGroups * */ SMARTCARD *sc; struct stream *s; int bytes; int bytes_groups = 0; // Length of NDR for groups + 2 terminators int val = 0; // Referent Id for mszGroups (assume NULL) int groups_len = 0; // strlen(groups) tui32 ioctl; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } ioctl = (wide) ? SCARD_IOCTL_LIST_READERS_W : SCARD_IOCTL_LIST_READERS_A; if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } if (groups != NULL && *groups != '\0') { groups_len = g_strlen(groups); if (wide) { bytes_groups = (utf8_as_utf16_word_count(groups, groups_len) + 2) * 2; } else { bytes_groups = groups_len + 2; } val = 0x00020004; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); // REDIR_SCARDCONTEXT Context; out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); // [range(0, 65536)] unsigned long cBytes; out_uint32_le(s, bytes_groups); // [unique] [size_is(cBytes)] const byte *mszGroups; (pointer) out_uint32_le(s, val); // long fmszReadersIsNULL; out_uint32_le(s, 0x00000000); // unsigned long cchReaders; out_uint32_le(s, cchReaders); // At the end of the struct come the pointed-to structures // Context field pbContext is a Uni-dimensional conformant array out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); // mszGroups is also a Uni-dimensional conformant array of bytes if (bytes_groups > 0) { align_s(s, 4); out_uint32_le(s, bytes_groups); if (wide) { out_utf8_as_utf16_le(s, groups, groups_len); out_uint16_le(s, 0); out_uint16_le(s, 0); } else { out_uint8p(s, groups, groups_len); out_uint8(s, 0); out_uint8(s, 0); } } s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_ListReaders:", s->data, bytes); free_stream(s); } /*****************************************************************************/ /** * Outputs the pointed-to-data for one of these IDL pointer types:- * [string] const wchar_t* str; (wide != 0) * [string] const char* str; (wide == 0) * * It is assumed that the referent identifier for the string has already * been sent * * @param s Output stream * @param str UTF-8 string to output * @param wide Whether to output as a wide string or ASCII * * Note that wchar_t on Windows is 16-bit * TODO: These strings have two terminators. Is this necessary? */ static void out_conformant_and_varying_string(struct stream *s, const char *str, int wide) { align_s(s, 4); unsigned int len = strlen(str); if (wide) { unsigned int num_chars = utf8_as_utf16_word_count(str, len); // Max number, offset and actual count out_uint32_le(s, num_chars + 2); out_uint32_le(s, 0); out_uint32_le(s, num_chars + 2); out_utf8_as_utf16_le(s, str, len); out_uint16_le(s, 0); // Terminate string out_uint16_le(s, 0); // ? } else { out_uint32_le(s, len + 2); out_uint32_le(s, 0); out_uint32_le(s, len + 2); out_uint8p(s, str, len); out_uint8(s, 0); out_uint8(s, 0); } } /** * Get change in status * * @param irp I/O resource pkt * @param wide TRUE if unicode string * @param timeout timeout in milliseconds, -1 for infinity * @param num_readers number of entries in rsa * @param rsa array of READER_STATEs *****************************************************************************/ static void scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes, int wide, tui32 timeout, tui32 num_readers, READER_STATE *rsa) { /* see [MS-RDPESC] 2.2.2.11 for ASCII * see [MS-RDPESC] 2.2.2.12 for Wide char * * Here is a breakdown of the Wide-char variant * * IDL:- * * typedef struct _REDIR_SCARDCONTEXT { * [range(0,16)] unsigned long cbContext; * [unique] [size_is(cbContext)] byte *pbContext; * } REDIR_SCARDCONTEXT; * * typedef struct _ReaderState_Common_Call { * unsigned long dwCurrentState; * unsigned long dwEventState; * [range(0,36)] unsigned long cbAtr; * byte rgbAtr[36]; * } ReaderState_Common_Call; * * typedef struct _ReaderStateW { * [string] const wchar_t* szReader; * ReaderState_Common_Call Common; * } ReaderStateW; * * struct _GetStatusChangeW_Call { * REDIR_SCARDCONTEXT Context; * unsigned long dwTimeOut; * [range(0,11)] unsigned long cReaders; * [size_is(cReaders)] ReaderStateW* rgReaderStates; * } GetStatusChangeW_Call; * * Type summary:- * * Context.cbContext Unsigned 32-bit word * Context.pbContext Embedded full pointer to conformant array of bytes * dwTimeOut Unsigned 32-bit word * cReaders Unsigned 32-bit word * rgReaderStates * Embedded full pointer to array of rgReaderStates * rgReaderStates.szReader * Embedded full pointer to conformant and varying * string of [Windows] wchar_t * rgReaderStates.Common.dwCurrentState * Unsigned 32-bit word * rgReaderStates.Common.dwEventState * Unsigned 32-bit word * rgReaderStates.Common.cbAtr * Unsigned 32-bit word * rgReaderStates.Common.rgbAtr[36] * Uni-dimensional fixed array * * NDL:- * Offset Decription * 0 Context.cbContext * 4 Referent Identifier for pbContext * 8 dwTimeOut; * 12 cReaders; * 16 Referent Identifier for rgReaderStates * 20 Conformant Array pointed to by pbContext * ?? Conformant Array pointed to by rgReaderStates. Each element * of this array has a pointer to a string for the name * ?? String names pointed to in the above array. */ SMARTCARD *sc; READER_STATE *rs; struct stream *s; tui32 ioctl; int bytes; unsigned int i; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } ioctl = (wide) ? SCARD_IOCTL_GET_STATUS_CHANGE_W : SCARD_IOCTL_GET_STATUS_CHANGE_A; if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); // REDIR_SCARDCONTEXT Context; out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); // unsigned long dwTimeOut; out_uint32_le(s, timeout); // [range(0,11)] unsigned long cReaders; out_uint32_le(s, num_readers); // [size_is(cReaders)] ReaderStateW* rgReaderStates; out_uint32_le(s, 0x00020004); // At the end of the struct come the pointed-to structures // Context field pbContext is a Uni-dimensional conformant array out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); // rgReaderState is a Uni-dimensional conformant array align_s(s, 4); out_uint32_le(s, num_readers); /* insert card reader state */ for (i = 0; i < num_readers; i++) { rs = &rsa[i]; // [string] const wchar_t* szReader (wide) // [string] const char_t* szReader (ASCII) out_uint32_le(s, 0x00020008 + (i * 4)); // unsigned long dwCurrentState; out_uint32_le(s, rs->current_state); // unsigned long dwEventState; out_uint32_le(s, rs->event_state); // [range(0,36)] unsigned long cbAtr; out_uint32_le(s, rs->atr_len); // byte rgbAtr[36]; out_uint8p(s, rs->atr, 33); out_uint8s(s, 3); } /* insert card reader names */ for (i = 0; i < num_readers; i++) { rs = &rsa[i]; out_conformant_and_varying_string(s, rs->reader_name, wide); } s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_GetStatusChange:", s->data, bytes); free_stream(s); } /** * Send connect command * * @param irp I/O resource pkt * @param wide TRUE if unicode string * @param rs reader state *****************************************************************************/ static void scard_send_Connect(IRP *irp, char *context, int context_bytes, int wide, READER_STATE *rs) { /* see [MS-RDPESC] 2.2.2.13 for ASCII * see [MS-RDPESC] 2.2.2.14 for Wide char * * Here is a breakdown of the Wide-char variant * * IDL:- * * typedef struct _REDIR_SCARDCONTEXT { * [range(0,16)] unsigned long cbContext; * [unique] [size_is(cbContext)] byte *pbContext; * } REDIR_SCARDCONTEXT; * * typedef struct _Connect_Common { * REDIR_SCARDCONTEXT Context; * unsigned long dwShareMode; * unsigned long dwPreferredProtocols; * } Connect_Common; * * typedef struct _ConnectW_Call { * [string] const wchar_t* szReader; * Connect_Common Common; * } ConnectW_Call; * * Type summary:- * * szReader Embedded full pointer to conformant and varying * string of [Windows] wchar_t * Common.Context.cbContext * Unsigned 32-bit word * Common.Context.pbContext * Embedded full pointer to conformant array of bytes * Common.dwShareMode Unsigned 32-bit word * Common.dwPreferredProtocols * Unsigned 32-bit word * * NDL:- * * Offset Decription * 0 Referent Identifier for szReader * 4 Context.cbContext * 8 Referent Identifier for pbContext * 12 dwShareMode * 16 dwPreferredProtocols * 20 Conformant Array pointed to by szReader * ?? Conformant Array pointed to by pbContext * */ SMARTCARD *sc; struct stream *s; tui32 ioctl; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } ioctl = (wide) ? SCARD_IOCTL_CONNECT_W : SCARD_IOCTL_CONNECT_A; if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); // [string] const wchar_t* szReader; out_uint32_le(s, 0x00020000); // REDIR_SCARDCONTEXT Context; out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020004); // unsigned long dwShareMode; out_uint32_le(s, rs->dwShareMode); // unsigned long dwPreferredProtocols; out_uint32_le(s, rs->dwPreferredProtocols); /* insert card reader name */ out_conformant_and_varying_string(s, rs->reader_name, wide); /* insert context */ align_s(s, 4); out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); out_uint32_le(s, 0); // ? s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * The reconnect method re-establishes a smart card reader handle. On success, * the handle is valid once again. * * @param con connection to client * @param sc_handle handle to device * @param rs reader state where following fields are set * rs.shared_mode_flag * rs.preferred_protocol * rs.init_type *****************************************************************************/ static void scard_send_Reconnect(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, READER_STATE *rs) { /* see [MS-RDPESC] 2.2.2.15 */ /* see [MS-RDPESC] 3.1.4.36 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_RECONNECT)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } /* * command format * * ...... * 20 bytes padding * u32 4 bytes len 8, LE, v1 * u32 4 bytes filler * 24 bytes unused (s->p currently pointed here at unused[0]) * u32 4 bytes dwShareMode * u32 4 bytes dwPreferredProtocols * u32 4 bytes dwInitialization * u32 4 bytes context length * u32 4 bytes context * u32 4 bytes handle length * u32 4 bytes handle */ xstream_seek(s, 24); /* TODO */ out_uint32_le(s, rs->dwShareMode); out_uint32_le(s, rs->dwPreferredProtocols); out_uint32_le(s, rs->init_type); out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); s_mark_end(s); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * Lock smart card reader for exclusive access for specified smart * card reader handle. * * @param con connection to client *****************************************************************************/ static void scard_send_BeginTransaction(IRP *irp, char *context, int context_bytes, char *card, int card_bytes) { /* see [MS-RDPESC] 4.9 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_BEGIN_TRANSACTION)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); out_uint32_le(s, card_bytes); out_uint32_le(s, 0x00020004); out_uint32_le(s, 0x00000000); /* insert context */ out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); /* insert card */ out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); out_uint32_le(s, 0x00000000); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * Release a smart card reader after being locked by a previously * successful call to Begin Transaction * * @param con connection to client * @param sc_handle handle to smartcard *****************************************************************************/ static void scard_send_EndTransaction(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, tui32 dwDisposition) { /* see [MS-RDPESC] 3.1.4.32 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_END_TRANSACTION)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); out_uint32_le(s, card_bytes); out_uint32_le(s, 0x00020004); out_uint32_le(s, dwDisposition); /* insert context */ out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); /* insert card */ out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); out_uint32_le(s, 0); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * Get the status of a connection for a valid smart card reader handle * * @param con connection to client * @param wide TRUE if unicode string *****************************************************************************/ static void scard_send_Status(IRP *irp, int wide, char *context, int context_bytes, char *card, int card_bytes, int cchReaderLen, int cbAtrLen) { /* see [MS-RDPESC] 2.2.2.18 */ SMARTCARD *sc; struct stream *s; int bytes; tui32 ioctl; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } ioctl = wide ? SCARD_IOCTL_STATUS_W : SCARD_IOCTL_STATUS_A; if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl"); return; } /* 30 00 00 00 00 00 00 00 04 00 00 00 00 00 02 00 04 00 00 00 04 00 02 00 01 00 00 00 00 00 00 00 dwReaderLen 40 00 00 00 dwAtrLen 04 00 00 00 07 00 00 00 04 00 00 00 09 00 00 00 hCard 00 00 00 00 */ s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); out_uint32_le(s, card_bytes); out_uint32_le(s, 0x00020004); out_uint32_le(s, 0x00000001); out_uint32_le(s, cchReaderLen); /* readerLen, see [MS-RDPESC] 4.11 */ out_uint32_le(s, cbAtrLen); /* atrLen, see [MS-RDPESC] 4.11 */ /* insert context */ out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); /* insert card */ out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); out_uint32_le(s, 0); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", s->data, bytes); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * Release a smart card reader handle that was acquired in ConnectA/ConnectW * * @param con connection to client * @param sc_handle handle to smartcard *****************************************************************************/ static void scard_send_Disconnect(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, int dwDisposition) { /* see [MS-RDPESC] 3.1.4.30 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_DISCONNECT)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed"); return; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); out_uint32_le(s, card_bytes); out_uint32_le(s, 0x00020004); out_uint32_le(s, dwDisposition); /* insert context */ out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); /* insert card */ out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); out_uint32_le(s, 0x00000000); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); } /** * The Transmit_Call structure is used to send data to the smart card * associated with a valid context. *****************************************************************************/ static int scard_send_Transmit(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, char *send_data, int send_bytes, int recv_bytes, struct xrdp_scard_io_request *send_ior, struct xrdp_scard_io_request *recv_ior) { /* see [MS-RDPESC] 2.2.2.19 */ SMARTCARD *sc; struct stream *s; int bytes; int val; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return 1; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_TRANSMIT)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl"); return 1; } LOG_DEVEL(LOG_LEVEL_DEBUG, "send_bytes %d recv_bytes %d send dwProtocol %d cbPciLength %d " "extra_bytes %d recv dwProtocol %d cbPciLength %d extra_bytes %d", send_bytes, recv_bytes, send_ior->dwProtocol, send_ior->cbPciLength, send_ior->extra_bytes, recv_ior->dwProtocol, recv_ior->cbPciLength, recv_ior->extra_bytes); /* * command format * * ...... * 20 bytes padding * u32 4 bytes len 8, LE, v1 * u32 4 bytes filler * 12 bytes unused (s->p currently pointed here at unused[0]) * u32 4 bytes map0 * 4 bytes unused * u32 4 bytes map1 * u32 4 bytes dwProtocol * u32 4 bytes cbPciLength * u32 4 bytes map2 * u32 4 bytes cbSendLength * u32 4 bytes map3 * u32 4 bytes map4 * u32 4 bytes map5 * u32 4 bytes map6 * u32 4 bytes cbRecvLength * u32 4 bytes len of sc_handle * u32 4 bytes sc_handle */ //g_writeln("send_bytes %d", send_bytes); //g_writeln("recv_bytes %d", recv_bytes); #if 0 s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_be(s, 0x00000000); out_uint32_be(s, 0x04000000); out_uint32_be(s, 0x00000200); // map 0 out_uint32_be(s, 0x04000000); out_uint32_be(s, 0x04000200); // map 1 out_uint32_be(s, 0x01000000); out_uint32_be(s, 0x00000000); out_uint32_be(s, 0x00000000); //out_uint32_be(s, 0x05000000); out_uint32_le(s, send_bytes); out_uint32_be(s, 0x08000200); out_uint32_be(s, 0x0c000200); out_uint32_be(s, 0x00000000); //out_uint32_be(s, 0x02010000); out_uint32_le(s, recv_bytes); out_uint32_be(s, 0x04000000); out_uint32_be(s, 0x05000000); out_uint32_be(s, 0x04000000); out_uint32_be(s, 0x0b000000); //out_uint32_be(s, 0x05000000); //out_uint32_be(s, 0x00b00704); //out_uint32_be(s, 0x10000000); out_uint32_le(s, send_bytes); out_uint8p(s, send_data, send_bytes); align_s(s, 4); out_uint32_be(s, 0x01000000); out_uint32_be(s, 0x00000000); out_uint32_be(s, 0x00000000); #else //g_printf("send cbPciLength %d\n", send_ior->cbPciLength); //g_printf("send extra_bytes %d\n", send_ior->extra_bytes); //g_printf("recv cbPciLength %d\n", recv_ior->cbPciLength); //g_printf("recv extra_bytes %d\n", recv_ior->extra_bytes); s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); /* map0 */ out_uint32_le(s, card_bytes); out_uint32_le(s, 0x00020004); /* map1 */ out_uint32_le(s, send_ior->dwProtocol); out_uint32_le(s, send_ior->cbPciLength - 8); val = send_ior->extra_bytes > 0 ? 1 : 0; out_uint32_le(s, val); /* map2 */ out_uint32_le(s, send_bytes); val = send_bytes > 0 ? 0x00020008 : 0; out_uint32_le(s, val); /* map3 */ val = recv_ior->cbPciLength > 0 ? 0x0002000c : 0; out_uint32_le(s, val); /* map 4 */ out_uint32_le(s, 0); // map5 out_uint32_le(s, recv_bytes); /* map0 */ out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); /* map1 */ out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); if (send_ior->extra_bytes > 0) { out_uint32_le(s, send_ior->extra_bytes); out_uint8a(s, send_ior->extra_data, send_ior->extra_bytes); } if (send_bytes > 0) { out_uint32_le(s, send_bytes); out_uint8a(s, send_data, send_bytes); align_s(s, 4); } if (recv_ior->cbPciLength > 0) { /* map4 */ out_uint32_le(s, recv_ior->dwProtocol); out_uint32_le(s, recv_ior->cbPciLength - 8); val = recv_ior->extra_bytes > 0 ? 1 : 0; out_uint32_le(s, val); /* map6*/ if (val) { out_uint32_le(s, recv_ior->extra_bytes); out_uint8a(s, recv_ior->extra_data, recv_ior->extra_bytes); } } #endif s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_Transmit:", s->data, bytes); free_stream(s); return 0; } /** * Communicate directly with the smart card reader *****************************************************************************/ static int scard_send_Control(IRP *irp, char *context, int context_bytes, char *card, int card_bytes, char *send_data, int send_bytes, int recv_bytes, int control_code) { /* see [MS-RDPESC] 2.2.2.19 */ SMARTCARD *sc; struct stream *s; int bytes; int val; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return 1; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_CONTROL)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl"); return 1; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); /* map0 */ out_uint32_le(s, card_bytes); out_uint32_le(s, 0x00020004); /* map1 */ out_uint32_le(s, control_code); out_uint32_le(s, send_bytes); val = send_bytes > 0 ? 0x00020008 : 0; out_uint32_le(s, val); /* map2 */ out_uint32_le(s, 0x00000000); out_uint32_le(s, recv_bytes); out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); if (send_bytes > 0) { out_uint32_le(s, send_bytes); out_uint8a(s, send_data, send_bytes); } else { out_uint32_le(s, 0x00000000); } s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", s->data, bytes); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); return 0; } /** * Cancel any outstanding calls *****************************************************************************/ static int scard_send_Cancel(IRP *irp, char *context, int context_bytes) { /* see [MS-RDPESC] 3.1.4.27 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return 1; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_CANCEL)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl"); return 1; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ out_uint32_le(s, 0x00000000); out_uint32_le(s, context_bytes); out_uint32_le(s, 0x00020000); out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); s_mark_end(s); s_pop_layer(s, mcs_hdr); bytes = (int) (s->end - s->p); bytes -= 8; out_uint32_le(s, bytes); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); return 0; } /** * Get reader attributes *****************************************************************************/ static int scard_send_GetAttrib(IRP *irp, char *card, int card_bytes, READER_STATE *rs) { /* see [MS-RDPESC] 2.2.2.21 */ SMARTCARD *sc; struct stream *s; int bytes; if ((sc = smartcards[irp->scard_index]) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index); return 1; } if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_GETATTRIB)) == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl"); return 1; } /* * command format * * ...... * 20 bytes padding * u32 4 bytes len 8, LE, v1 * u32 4 bytes filler * 24 bytes unused (s->p currently pointed here at unused[0]) * u32 4 bytes dwAttribId * 4 bytes unused * u32 4 bytes dwAttrLen * 8 bytes unused * u32 4 bytes handle len * u32 4 bytes handle */ xstream_seek(s, 24); /* TODO */ out_uint32_le(s, rs->dwAttribId); out_uint32_le(s, 0); out_uint32_le(s, rs->dwAttrLen); xstream_seek(s, 8); out_uint32_le(s, card_bytes); out_uint8a(s, card, card_bytes); s_mark_end(s); s_pop_layer(s, iso_hdr); bytes = (int) (s->end - s->p); bytes -= 28; out_uint32_le(s, bytes); bytes = (int) (s->end - s->data); /* send to client */ send_channel_data(g_rdpdr_chan_id, s->data, bytes); free_stream(s); return 0; } /****************************************************************************** ** ** ** local callbacks into this module ** ** ** ******************************************************************************/ /** * *****************************************************************************/ static void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_establish_context_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_ReleaseContext_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_release_context_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_IsContextValid_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_is_context_valid_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_ListReaders_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_list_readers_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_GetStatusChange_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_get_status_change_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Connect_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_connect_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Reconnect_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_reconnect_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_BeginTransaction_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_begin_transaction_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_EndTransaction_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_end_transaction_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Status_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_status_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Disconnect_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_disconnect_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Transmit_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_transmit_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Control_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_control_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_Cancel_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_cancel_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); } /** * *****************************************************************************/ static void scard_handle_GetAttrib_Return(struct stream *s, IRP *irp, tui32 DeviceId, tui32 CompletionId, tui32 IoStatus) { tui32 len; LOG_DEVEL(LOG_LEVEL_DEBUG, "entered"); /* sanity check */ if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) { LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP"); return; } /* get OutputBufferLen */ xstream_rd_u32_le(s, len); scard_function_get_attrib_return(irp->user_data, s, len, IoStatus); devredir_irp_delete(irp); LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving"); }