/** * FreeRDP: A Remote Desktop Protocol Implementation * Smartcard Device Service Virtual Channel * * Copyright (C) Alexi Volkov 2006 * Copyright 2011 O.S. Systems Software Ltda. * Copyright 2011 Anthony Tong * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger * Copyright 2017 Armin Novak * Copyright 2017 Thincast Technologies GmbH * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #define TAG FREERDP_TAG("utils.smartcard.ops") static LONG smartcard_call_to_operation_handle(SMARTCARD_OPERATION* operation) { WINPR_ASSERT(operation); operation->hContext = smartcard_scard_context_native_from_redir(&(operation->call.handles.hContext)); operation->hCard = smartcard_scard_handle_native_from_redir(&(operation->call.handles.hCard)); return SCARD_S_SUCCESS; } static LONG smartcard_EstablishContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_establish_context_call(s, &operation->call.establishContext); if (status != SCARD_S_SUCCESS) { return scard_log_status_error(TAG, "smartcard_unpack_establish_context_call", status); } return SCARD_S_SUCCESS; } static LONG smartcard_ReleaseContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_call(s, &operation->call.context, "ReleaseContext"); if (status != SCARD_S_SUCCESS) scard_log_status_error(TAG, "smartcard_unpack_context_call", status); return status; } static LONG smartcard_IsValidContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_call(s, &operation->call.context, "IsValidContext"); return status; } static LONG smartcard_ListReaderGroupsA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_list_reader_groups_call(s, &operation->call.listReaderGroups, FALSE); return status; } static LONG smartcard_ListReaderGroupsW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_list_reader_groups_call(s, &operation->call.listReaderGroups, TRUE); return status; } static LONG smartcard_ListReadersA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_list_readers_call(s, &operation->call.listReaders, FALSE); return status; } static LONG smartcard_ListReadersW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_list_readers_call(s, &operation->call.listReaders, TRUE); return status; } static LONG smartcard_context_and_two_strings_a_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_and_two_strings_a_call(s, &operation->call.contextAndTwoStringA); return status; } static LONG smartcard_context_and_two_strings_w_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_and_two_strings_w_call(s, &operation->call.contextAndTwoStringW); return status; } static LONG smartcard_context_and_string_a_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_and_string_a_call(s, &operation->call.contextAndStringA); return status; } static LONG smartcard_context_and_string_w_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_and_string_w_call(s, &operation->call.contextAndStringW); return status; } static LONG smartcard_LocateCardsA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_locate_cards_a_call(s, &operation->call.locateCardsA); return status; } static LONG smartcard_LocateCardsW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_locate_cards_w_call(s, &operation->call.locateCardsW); return status; } static LONG smartcard_GetStatusChangeA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_get_status_change_a_call(s, &operation->call.getStatusChangeA); return status; } static LONG smartcard_GetStatusChangeW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_get_status_change_w_call(s, &operation->call.getStatusChangeW); return status; } static LONG smartcard_Cancel_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_context_call(s, &operation->call.context, "Cancel"); return status; } static LONG smartcard_ConnectA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_connect_a_call(s, &operation->call.connectA); return status; } static LONG smartcard_ConnectW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_connect_w_call(s, &operation->call.connectW); return status; } static LONG smartcard_Reconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_reconnect_call(s, &operation->call.reconnect); return status; } static LONG smartcard_Disconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, "Disconnect"); return status; } static LONG smartcard_BeginTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, "BeginTransaction"); return status; } static LONG smartcard_EndTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, "EndTransaction"); return status; } static LONG smartcard_State_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_state_call(s, &operation->call.state); return status; } static LONG smartcard_StatusA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_status_call(s, &operation->call.status, FALSE); return status; } static LONG smartcard_StatusW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_status_call(s, &operation->call.status, TRUE); return status; } static LONG smartcard_Transmit_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_transmit_call(s, &operation->call.transmit); return status; } static LONG smartcard_Control_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_control_call(s, &operation->call.control); return status; } static LONG smartcard_GetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_get_attrib_call(s, &operation->call.getAttrib); return status; } static LONG smartcard_SetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_set_attrib_call(s, &operation->call.setAttrib); return status; } static LONG smartcard_AccessStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation) { WINPR_ASSERT(s); WINPR_ASSERT(operation); if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) return SCARD_F_INTERNAL_ERROR; Stream_Read_INT32(s, operation->call.lng.LongValue); /* Unused (4 bytes) */ return SCARD_S_SUCCESS; } static LONG smartcard_LocateCardsByATRA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_locate_cards_by_atr_a_call(s, &operation->call.locateCardsByATRA); return status; } static LONG smartcard_LocateCardsByATRW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_locate_cards_by_atr_w_call(s, &operation->call.locateCardsByATRW); return status; } static LONG smartcard_ReadCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_read_cache_a_call(s, &operation->call.readCacheA); return status; } static LONG smartcard_ReadCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_read_cache_w_call(s, &operation->call.readCacheW); return status; } static LONG smartcard_WriteCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_write_cache_a_call(s, &operation->call.writeCacheA); return status; } static LONG smartcard_WriteCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_write_cache_w_call(s, &operation->call.writeCacheW); return status; } static LONG smartcard_GetTransmitCount_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_get_transmit_count_call(s, &operation->call.getTransmitCount); return status; } static LONG smartcard_ReleaseStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation) { WINPR_UNUSED(s); WINPR_UNUSED(operation); WLog_WARN(TAG, "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules " "SCARD_IOCTL_RELEASETARTEDEVENT is not supported"); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG smartcard_GetReaderIcon_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_get_reader_icon_call(s, &operation->call.getReaderIcon); return status; } static LONG smartcard_GetDeviceTypeId_Decode(wStream* s, SMARTCARD_OPERATION* operation) { LONG status; WINPR_ASSERT(s); WINPR_ASSERT(operation); status = smartcard_unpack_get_device_type_id_call(s, &operation->call.getDeviceTypeId); return status; } LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 FileId, SMARTCARD_OPERATION* operation) { LONG status; UINT32 offset; UINT32 ioControlCode; UINT32 outputBufferLength; UINT32 inputBufferLength; WINPR_ASSERT(s); WINPR_ASSERT(operation); /* Device Control Request */ if (!Stream_CheckAndLogRequiredLength(TAG, s, 32)) return SCARD_F_INTERNAL_ERROR; Stream_Read_UINT32(s, outputBufferLength); /* OutputBufferLength (4 bytes) */ Stream_Read_UINT32(s, inputBufferLength); /* InputBufferLength (4 bytes) */ Stream_Read_UINT32(s, ioControlCode); /* IoControlCode (4 bytes) */ Stream_Seek(s, 20); /* Padding (20 bytes) */ operation->ioControlCode = ioControlCode; operation->ioControlCodeName = scard_get_ioctl_string(ioControlCode, FALSE); if (Stream_Length(s) != (Stream_GetPosition(s) + inputBufferLength)) { WLog_WARN(TAG, "InputBufferLength mismatch: Actual: %" PRIuz " Expected: %" PRIuz "", Stream_Length(s), Stream_GetPosition(s) + inputBufferLength); return SCARD_F_INTERNAL_ERROR; } WLog_DBG(TAG, "%s (0x%08" PRIX32 ") FileId: %" PRIu32 " CompletionId: %" PRIu32 "", scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, FileId, CompletionId); if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) && (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) { status = smartcard_unpack_common_type_header(s); if (status != SCARD_S_SUCCESS) return status; status = smartcard_unpack_private_type_header(s); if (status != SCARD_S_SUCCESS) return status; } /* Decode */ switch (ioControlCode) { case SCARD_IOCTL_ESTABLISHCONTEXT: status = smartcard_EstablishContext_Decode(s, operation); break; case SCARD_IOCTL_RELEASECONTEXT: status = smartcard_ReleaseContext_Decode(s, operation); break; case SCARD_IOCTL_ISVALIDCONTEXT: status = smartcard_IsValidContext_Decode(s, operation); break; case SCARD_IOCTL_LISTREADERGROUPSA: status = smartcard_ListReaderGroupsA_Decode(s, operation); break; case SCARD_IOCTL_LISTREADERGROUPSW: status = smartcard_ListReaderGroupsW_Decode(s, operation); break; case SCARD_IOCTL_LISTREADERSA: status = smartcard_ListReadersA_Decode(s, operation); break; case SCARD_IOCTL_LISTREADERSW: status = smartcard_ListReadersW_Decode(s, operation); break; case SCARD_IOCTL_INTRODUCEREADERGROUPA: status = smartcard_context_and_string_a_Decode(s, operation); break; case SCARD_IOCTL_INTRODUCEREADERGROUPW: status = smartcard_context_and_string_w_Decode(s, operation); break; case SCARD_IOCTL_FORGETREADERGROUPA: status = smartcard_context_and_string_a_Decode(s, operation); break; case SCARD_IOCTL_FORGETREADERGROUPW: status = smartcard_context_and_string_w_Decode(s, operation); break; case SCARD_IOCTL_INTRODUCEREADERA: status = smartcard_context_and_two_strings_a_Decode(s, operation); break; case SCARD_IOCTL_INTRODUCEREADERW: status = smartcard_context_and_two_strings_w_Decode(s, operation); break; case SCARD_IOCTL_FORGETREADERA: status = smartcard_context_and_string_a_Decode(s, operation); break; case SCARD_IOCTL_FORGETREADERW: status = smartcard_context_and_string_w_Decode(s, operation); break; case SCARD_IOCTL_ADDREADERTOGROUPA: status = smartcard_context_and_two_strings_a_Decode(s, operation); break; case SCARD_IOCTL_ADDREADERTOGROUPW: status = smartcard_context_and_two_strings_w_Decode(s, operation); break; case SCARD_IOCTL_REMOVEREADERFROMGROUPA: status = smartcard_context_and_two_strings_a_Decode(s, operation); break; case SCARD_IOCTL_REMOVEREADERFROMGROUPW: status = smartcard_context_and_two_strings_w_Decode(s, operation); break; case SCARD_IOCTL_LOCATECARDSA: status = smartcard_LocateCardsA_Decode(s, operation); break; case SCARD_IOCTL_LOCATECARDSW: status = smartcard_LocateCardsW_Decode(s, operation); break; case SCARD_IOCTL_GETSTATUSCHANGEA: status = smartcard_GetStatusChangeA_Decode(s, operation); break; case SCARD_IOCTL_GETSTATUSCHANGEW: status = smartcard_GetStatusChangeW_Decode(s, operation); break; case SCARD_IOCTL_CANCEL: status = smartcard_Cancel_Decode(s, operation); break; case SCARD_IOCTL_CONNECTA: status = smartcard_ConnectA_Decode(s, operation); break; case SCARD_IOCTL_CONNECTW: status = smartcard_ConnectW_Decode(s, operation); break; case SCARD_IOCTL_RECONNECT: status = smartcard_Reconnect_Decode(s, operation); break; case SCARD_IOCTL_DISCONNECT: status = smartcard_Disconnect_Decode(s, operation); break; case SCARD_IOCTL_BEGINTRANSACTION: status = smartcard_BeginTransaction_Decode(s, operation); break; case SCARD_IOCTL_ENDTRANSACTION: status = smartcard_EndTransaction_Decode(s, operation); break; case SCARD_IOCTL_STATE: status = smartcard_State_Decode(s, operation); break; case SCARD_IOCTL_STATUSA: status = smartcard_StatusA_Decode(s, operation); break; case SCARD_IOCTL_STATUSW: status = smartcard_StatusW_Decode(s, operation); break; case SCARD_IOCTL_TRANSMIT: status = smartcard_Transmit_Decode(s, operation); break; case SCARD_IOCTL_CONTROL: status = smartcard_Control_Decode(s, operation); break; case SCARD_IOCTL_GETATTRIB: status = smartcard_GetAttrib_Decode(s, operation); break; case SCARD_IOCTL_SETATTRIB: status = smartcard_SetAttrib_Decode(s, operation); break; case SCARD_IOCTL_ACCESSSTARTEDEVENT: status = smartcard_AccessStartedEvent_Decode(s, operation); break; case SCARD_IOCTL_LOCATECARDSBYATRA: status = smartcard_LocateCardsByATRA_Decode(s, operation); break; case SCARD_IOCTL_LOCATECARDSBYATRW: status = smartcard_LocateCardsByATRW_Decode(s, operation); break; case SCARD_IOCTL_READCACHEA: status = smartcard_ReadCacheA_Decode(s, operation); break; case SCARD_IOCTL_READCACHEW: status = smartcard_ReadCacheW_Decode(s, operation); break; case SCARD_IOCTL_WRITECACHEA: status = smartcard_WriteCacheA_Decode(s, operation); break; case SCARD_IOCTL_WRITECACHEW: status = smartcard_WriteCacheW_Decode(s, operation); break; case SCARD_IOCTL_GETTRANSMITCOUNT: status = smartcard_GetTransmitCount_Decode(s, operation); break; case SCARD_IOCTL_RELEASETARTEDEVENT: status = smartcard_ReleaseStartedEvent_Decode(s, operation); break; case SCARD_IOCTL_GETREADERICON: status = smartcard_GetReaderIcon_Decode(s, operation); break; case SCARD_IOCTL_GETDEVICETYPEID: status = smartcard_GetDeviceTypeId_Decode(s, operation); break; default: status = SCARD_F_INTERNAL_ERROR; break; } smartcard_call_to_operation_handle(operation); if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) && (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) { offset = (RDPDR_DEVICE_IO_REQUEST_LENGTH + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH); smartcard_unpack_read_size_align(s, Stream_GetPosition(s) - offset, 8); } if (Stream_GetPosition(s) < Stream_Length(s)) { SIZE_T difference; difference = Stream_Length(s) - Stream_GetPosition(s); WLog_WARN(TAG, "IRP was not fully parsed %s (%s [0x%08" PRIX32 "]): Actual: %" PRIuz ", Expected: %" PRIuz ", Difference: %" PRIuz "", scard_get_ioctl_string(ioControlCode, TRUE), scard_get_ioctl_string(ioControlCode, FALSE), ioControlCode, Stream_GetPosition(s), Stream_Length(s), difference); winpr_HexDump(TAG, WLOG_WARN, Stream_ConstPointer(s), difference); } if (Stream_GetPosition(s) > Stream_Length(s)) { SIZE_T difference; difference = Stream_GetPosition(s) - Stream_Length(s); WLog_WARN(TAG, "IRP was parsed beyond its end %s (0x%08" PRIX32 "): Actual: %" PRIuz ", Expected: %" PRIuz ", Difference: %" PRIuz "", scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, Stream_GetPosition(s), Stream_Length(s), difference); } return status; } static void free_reader_states_a(LPSCARD_READERSTATEA rgReaderStates, UINT32 cReaders) { UINT32 x; for (x = 0; x < cReaders; x++) { SCARD_READERSTATEA* state = &rgReaderStates[x]; free(state->szReader); } free(rgReaderStates); } static void free_reader_states_w(LPSCARD_READERSTATEW rgReaderStates, UINT32 cReaders) { UINT32 x; for (x = 0; x < cReaders; x++) { SCARD_READERSTATEW* state = &rgReaderStates[x]; free(state->szReader); } free(rgReaderStates); } void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) { if (!op) return; switch (op->ioControlCode) { case SCARD_IOCTL_CANCEL: case SCARD_IOCTL_ACCESSSTARTEDEVENT: case SCARD_IOCTL_RELEASETARTEDEVENT: case SCARD_IOCTL_LISTREADERGROUPSA: case SCARD_IOCTL_LISTREADERGROUPSW: case SCARD_IOCTL_RECONNECT: case SCARD_IOCTL_DISCONNECT: case SCARD_IOCTL_BEGINTRANSACTION: case SCARD_IOCTL_ENDTRANSACTION: case SCARD_IOCTL_STATE: case SCARD_IOCTL_STATUSA: case SCARD_IOCTL_STATUSW: case SCARD_IOCTL_ESTABLISHCONTEXT: case SCARD_IOCTL_RELEASECONTEXT: case SCARD_IOCTL_ISVALIDCONTEXT: case SCARD_IOCTL_GETATTRIB: case SCARD_IOCTL_GETTRANSMITCOUNT: break; case SCARD_IOCTL_LOCATECARDSA: { LocateCardsA_Call* call = &op->call.locateCardsA; free(call->mszCards); free_reader_states_a(call->rgReaderStates, call->cReaders); } break; case SCARD_IOCTL_LOCATECARDSW: { LocateCardsW_Call* call = &op->call.locateCardsW; free(call->mszCards); free_reader_states_w(call->rgReaderStates, call->cReaders); } break; case SCARD_IOCTL_LOCATECARDSBYATRA: { LocateCardsByATRA_Call* call = &op->call.locateCardsByATRA; free_reader_states_a(call->rgReaderStates, call->cReaders); } break; case SCARD_IOCTL_LOCATECARDSBYATRW: { LocateCardsByATRW_Call* call = &op->call.locateCardsByATRW; free_reader_states_w(call->rgReaderStates, call->cReaders); } break; case SCARD_IOCTL_FORGETREADERA: case SCARD_IOCTL_INTRODUCEREADERGROUPA: case SCARD_IOCTL_FORGETREADERGROUPA: { ContextAndStringA_Call* call = &op->call.contextAndStringA; free(call->sz); } break; case SCARD_IOCTL_FORGETREADERW: case SCARD_IOCTL_INTRODUCEREADERGROUPW: case SCARD_IOCTL_FORGETREADERGROUPW: { ContextAndStringW_Call* call = &op->call.contextAndStringW; free(call->sz); } break; case SCARD_IOCTL_INTRODUCEREADERA: case SCARD_IOCTL_REMOVEREADERFROMGROUPA: case SCARD_IOCTL_ADDREADERTOGROUPA: { ContextAndTwoStringA_Call* call = &op->call.contextAndTwoStringA; free(call->sz1); free(call->sz2); } break; case SCARD_IOCTL_INTRODUCEREADERW: case SCARD_IOCTL_REMOVEREADERFROMGROUPW: case SCARD_IOCTL_ADDREADERTOGROUPW: { ContextAndTwoStringW_Call* call = &op->call.contextAndTwoStringW; free(call->sz1); free(call->sz2); } break; case SCARD_IOCTL_LISTREADERSA: case SCARD_IOCTL_LISTREADERSW: { ListReaders_Call* call = &op->call.listReaders; free(call->mszGroups); } break; case SCARD_IOCTL_GETSTATUSCHANGEA: { GetStatusChangeA_Call* call = &op->call.getStatusChangeA; free_reader_states_a(call->rgReaderStates, call->cReaders); } break; case SCARD_IOCTL_GETSTATUSCHANGEW: { GetStatusChangeW_Call* call = &op->call.getStatusChangeW; free_reader_states_w(call->rgReaderStates, call->cReaders); } break; case SCARD_IOCTL_GETREADERICON: { GetReaderIcon_Call* call = &op->call.getReaderIcon; free(call->szReaderName); } break; case SCARD_IOCTL_GETDEVICETYPEID: { GetDeviceTypeId_Call* call = &op->call.getDeviceTypeId; free(call->szReaderName); } break; case SCARD_IOCTL_CONNECTA: { ConnectA_Call* call = &op->call.connectA; free(call->szReader); } break; case SCARD_IOCTL_CONNECTW: { ConnectW_Call* call = &op->call.connectW; free(call->szReader); } break; case SCARD_IOCTL_SETATTRIB: free(op->call.setAttrib.pbAttr); break; case SCARD_IOCTL_TRANSMIT: { Transmit_Call* call = &op->call.transmit; free(call->pbSendBuffer); free(call->pioSendPci); free(call->pioRecvPci); } break; case SCARD_IOCTL_CONTROL: { Control_Call* call = &op->call.control; free(call->pvInBuffer); } break; case SCARD_IOCTL_READCACHEA: { ReadCacheA_Call* call = &op->call.readCacheA; free(call->szLookupName); free(call->Common.CardIdentifier); } break; case SCARD_IOCTL_READCACHEW: { ReadCacheW_Call* call = &op->call.readCacheW; free(call->szLookupName); free(call->Common.CardIdentifier); } break; case SCARD_IOCTL_WRITECACHEA: { WriteCacheA_Call* call = &op->call.writeCacheA; free(call->szLookupName); free(call->Common.CardIdentifier); free(call->Common.pbData); } break; case SCARD_IOCTL_WRITECACHEW: { WriteCacheW_Call* call = &op->call.writeCacheW; free(call->szLookupName); free(call->Common.CardIdentifier); free(call->Common.pbData); } break; default: break; } { SMARTCARD_OPERATION empty = { 0 }; *op = empty; } if (allocated) free(op); }