diff --git a/channels/smartcard/client/smartcard_operations.c b/channels/smartcard/client/smartcard_operations.c index 643ba97fb..24f046605 100644 --- a/channels/smartcard/client/smartcard_operations.c +++ b/channels/smartcard/client/smartcard_operations.c @@ -1032,6 +1032,108 @@ static UINT32 smartcard_AccessStartedEvent_Call(SMARTCARD_DEVICE* smartcard, SMA return status; } +static UINT32 smartcard_LocateCardsByATRA_Decode(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation, LocateCardsByATRA_Call* call) +{ + LONG status; + IRP* irp = operation->irp; + + if (!call) + return STATUS_NO_MEMORY; + + status = smartcard_unpack_locate_cards_by_atr_a_call(smartcard, irp->input, call); + smartcard_trace_locate_cards_by_atr_a_call(smartcard, call); + operation->hContext = smartcard_scard_context_native_from_redir(smartcard, &(call->hContext)); + return status; +} + +static UINT32 smartcard_LocateCardsByATRA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation, LocateCardsByATRA_Call* call) +{ + LONG status; + DWORD index, index2, index3; + BOOL equal; + GetStatusChange_Return ret; + LPSCARD_READERSTATEA rgReaderState2 = NULL; + LPSCARD_READERSTATEA states = NULL; + IRP* irp = operation->irp; + + states = calloc(call->cReaders, sizeof(SCARD_READERSTATEA)); + for (index = 0; index < call->cReaders; index++) + { + states[index].szReader = (LPCSTR) call->rgReaderStates[index].szReader; + states[index].dwCurrentState = call->rgReaderStates[index].Common.dwCurrentState; + states[index].dwEventState = call->rgReaderStates[index].Common.dwEventState; + states[index].cbAtr = call->rgReaderStates[index].Common.cbAtr; + CopyMemory(&(states[index].rgbAtr), &(call->rgReaderStates[index].Common.rgbAtr), 36); + } + + + status = ret.ReturnCode = SCardGetStatusChangeA(operation->hContext, 0x000001F4, states, call->cReaders); + + if (status && (status != SCARD_E_TIMEOUT) && (status != SCARD_E_CANCELLED)) + { + call->cReaders=0; + } + + for (index = 0; index < call->cAtrs; index++) + { + for (index2 = 0; index2 < call->cReaders; index2++) + { + equal = TRUE; + for (index3 = 0; index3 < call->rgAtrMasks[index].cbAtr; index3++) + { + if ((call->rgAtrMasks[index].rgbAtr[index3] & call->rgAtrMasks[index].rgbMask[index3]) != + (states[index2].rgbAtr[index3] & call->rgAtrMasks[index].rgbMask[index3])) + { + equal = FALSE; + break; + } + if (equal) + { + states[index2].dwEventState |= SCARD_STATE_ATRMATCH; + } + } + } + } + + ret.cReaders = call->cReaders; + ret.rgReaderStates = (ReaderState_Return*) calloc(ret.cReaders, sizeof(ReaderState_Return)); + + for (index = 0; index < ret.cReaders; index++) + { + rgReaderState2 = &states[index]; + ret.rgReaderStates[index].dwCurrentState = rgReaderState2->dwCurrentState; + ret.rgReaderStates[index].dwEventState = rgReaderState2->dwEventState; + ret.rgReaderStates[index].cbAtr = rgReaderState2->cbAtr; + CopyMemory(&(ret.rgReaderStates[index].rgbAtr), &(rgReaderState2->rgbAtr), 32); + } + free(states); + + smartcard_trace_get_status_change_return(smartcard, &ret, FALSE); + status = smartcard_pack_get_status_change_return(smartcard, irp->output, &ret); + + if (status) + return status; + + if (call->rgReaderStates) + { + for (index = 0; index < call->cReaders; index++) + { + rgReaderState2 = (LPSCARD_READERSTATEA) &call->rgReaderStates[index]; + + if (rgReaderState2->szReader) { + free((void*) rgReaderState2->szReader); + rgReaderState2->szReader = NULL; + } + } + + free(call->rgReaderStates); + call->rgReaderStates = NULL; + } + + free(ret.rgReaderStates); + return ret.ReturnCode; +} + UINT32 smartcard_irp_device_control_decode(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation) { UINT32 status; @@ -1264,7 +1366,8 @@ UINT32 smartcard_irp_device_control_decode(SMARTCARD_DEVICE* smartcard, SMARTCAR break; case SCARD_IOCTL_LOCATECARDSBYATRA: - status = SCARD_F_INTERNAL_ERROR; + call = calloc(1, sizeof(LocateCardsByATRA_Call)); + status = smartcard_LocateCardsByATRA_Decode(smartcard, operation, (LocateCardsByATRA_Call*) call); break; case SCARD_IOCTL_LOCATECARDSBYATRW: @@ -1530,7 +1633,7 @@ UINT32 smartcard_irp_device_control_call(SMARTCARD_DEVICE* smartcard, SMARTCARD_ break; case SCARD_IOCTL_LOCATECARDSBYATRA: - result = SCARD_F_INTERNAL_ERROR; + result = smartcard_LocateCardsByATRA_Call(smartcard, operation, (LocateCardsByATRA_Call*) call); break; case SCARD_IOCTL_LOCATECARDSBYATRW: diff --git a/channels/smartcard/client/smartcard_pack.c b/channels/smartcard/client/smartcard_pack.c index 10a891d2e..bb39c7815 100644 --- a/channels/smartcard/client/smartcard_pack.c +++ b/channels/smartcard/client/smartcard_pack.c @@ -2471,3 +2471,240 @@ void smartcard_trace_transmit_return(SMARTCARD_DEVICE* smartcard, Transmit_Retur WLog_Print(smartcard->log, WLOG_DEBUG, "}"); } + +UINT32 smartcard_unpack_locate_cards_by_atr_a_call(SMARTCARD_DEVICE* smartcard, wStream* s, LocateCardsByATRA_Call* call) +{ + UINT32 index; + UINT32 count; + UINT32 status; + UINT32 offset; + UINT32 maxCount; + UINT32 szReaderNdrPtr; + UINT32 rgReaderStatesNdrPtr; + UINT32 rgAtrMasksNdrPtr; + LPSCARD_READERSTATEA readerState; + + call->rgReaderStates = NULL; + + status = smartcard_unpack_redir_scard_context(smartcard, s, &(call->hContext)); + + if (status) + return status; + + if (Stream_GetRemainingLength(s) < 16) + { + WLog_Print(smartcard->log, WLOG_WARN, "LocateCardsByATRA_Call is too short: %d", + (int) Stream_GetRemainingLength(s)); + return STATUS_BUFFER_TOO_SMALL; + } + + Stream_Read_UINT32(s, call->cAtrs); + Stream_Read_UINT32(s, rgAtrMasksNdrPtr); + Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */ + Stream_Read_UINT32(s, rgReaderStatesNdrPtr); /* rgReaderStatesNdrPtr (4 bytes) */ + + status = smartcard_unpack_redir_scard_context_ref(smartcard, s, &(call->hContext)); + + if (status) + return status; + + if (Stream_GetRemainingLength(s) < 4) + { + WLog_Print(smartcard->log, WLOG_WARN, "LocateCardsByATRA_Call is too short: %d", + (int) Stream_GetRemainingLength(s)); + return STATUS_BUFFER_TOO_SMALL; + } + + if ((rgAtrMasksNdrPtr && !call->cAtrs) || (!rgAtrMasksNdrPtr && call->cAtrs)) + { + WLog_Print(smartcard->log, WLOG_WARN, + "LocateCardsByATRA_Call rgAtrMasksNdrPtr (0x%08X) and cAtrs (0x%08X) inconsistency", + (int) rgAtrMasksNdrPtr, (int) call->cAtrs); + return STATUS_INVALID_PARAMETER; + } + + if (rgAtrMasksNdrPtr) + { + Stream_Read_UINT32(s, count); + + if (count != call->cAtrs) + { + WLog_Print(smartcard->log, WLOG_WARN, + "LocateCardsByATRA_Call NdrCount (0x%08X) and cAtrs (0x%08X) inconsistency", + (int) count, (int) call->cAtrs); + return STATUS_INVALID_PARAMETER; + } + + if (Stream_GetRemainingLength(s) < call->cAtrs) + { + WLog_Print(smartcard->log, WLOG_WARN, "LocateCardsByATRA_Call is too short: Actual: %d, Expected: %d", + (int) Stream_GetRemainingLength(s), call->cAtrs); + return STATUS_BUFFER_TOO_SMALL; + } + + call->rgAtrMasks = calloc(call->cAtrs, sizeof(SCARD_ATRMASK)); + if (!call->rgAtrMasks) + { + WLog_Print(smartcard->log, WLOG_WARN, "LocateCardsByATRA_Call out of memory error (call->rgAtrMasks)"); + return STATUS_NO_MEMORY; + } + + for (index = 0; index < call->cAtrs; index++) + { + Stream_Read_UINT32(s, call->rgAtrMasks[index].cbAtr); + Stream_Read(s, call->rgAtrMasks[index].rgbAtr, 36); + Stream_Read(s, call->rgAtrMasks[index].rgbMask, 36); + } + } + + Stream_Read_UINT32(s, count); + + if (count != call->cReaders) + { + WLog_Print(smartcard->log, WLOG_WARN, + "GetStatusChangeA_Call unexpected reader count: Actual: %d, Expected: %d", + (int) count, call->cReaders); + return STATUS_INVALID_PARAMETER; + } + + if (call->cReaders > 0) + { + call->rgReaderStates = calloc(call->cReaders, sizeof(SCARD_READERSTATEA)); + + if (!call->rgReaderStates) + { + WLog_Print(smartcard->log, WLOG_WARN, "LocateCardsByATRA_Call out of memory error (call->rgReaderStates)"); + return STATUS_NO_MEMORY; + } + + for (index = 0; index < call->cReaders; index++) + { + readerState = (LPSCARD_READERSTATEA) &call->rgReaderStates[index]; + + if (Stream_GetRemainingLength(s) < 52) + { + WLog_Print(smartcard->log, WLOG_WARN, "LocateCardsByATRA_Call is too short: %d", + (int) Stream_GetRemainingLength(s)); + return STATUS_BUFFER_TOO_SMALL; + } + + Stream_Read_UINT32(s, szReaderNdrPtr); /* szReaderNdrPtr (4 bytes) */ + Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */ + Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */ + Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */ + Stream_Read(s, readerState->rgbAtr, 32); /* rgbAtr [0..32] (32 bytes) */ + Stream_Seek(s, 4); /* rgbAtr [32..36] (4 bytes) */ + } + + for (index = 0; index < call->cReaders; index++) + { + readerState = (LPSCARD_READERSTATEA) &call->rgReaderStates[index]; + + if (Stream_GetRemainingLength(s) < 12) + { + WLog_Print(smartcard->log, WLOG_WARN, "GetStatusChangeA_Call is too short: %d", + (int) Stream_GetRemainingLength(s)); + return STATUS_BUFFER_TOO_SMALL; + } + + Stream_Read_UINT32(s, maxCount); /* NdrMaxCount (4 bytes) */ + Stream_Read_UINT32(s, offset); /* NdrOffset (4 bytes) */ + Stream_Read_UINT32(s, count); /* NdrActualCount (4 bytes) */ + + if (Stream_GetRemainingLength(s) < count) + { + WLog_Print(smartcard->log, WLOG_WARN, "GetStatusChangeA_Call is too short: %d", + (int) Stream_GetRemainingLength(s)); + return STATUS_BUFFER_TOO_SMALL; + } + + readerState->szReader = (LPCSTR) malloc(count + 1); + + if (!readerState->szReader) + { + WLog_Print(smartcard->log, WLOG_WARN, + "GetStatusChangeA_Call out of memory error (readerState->szReader)"); + return STATUS_NO_MEMORY; + } + + Stream_Read(s, (void*) readerState->szReader, count); + smartcard_unpack_read_size_align(smartcard, s, count, 4); + ((char*) readerState->szReader)[count] = '\0'; + + if (!readerState->szReader) + { + WLog_Print(smartcard->log, WLOG_WARN, "GetStatusChangeA_Call null reader name"); + return STATUS_INVALID_PARAMETER; + } + } + } + + return SCARD_S_SUCCESS; +} + +void smartcard_trace_locate_cards_by_atr_a_call(SMARTCARD_DEVICE* smartcard, LocateCardsByATRA_Call* call) +{ + BYTE* pb; + UINT32 index; + char* szEventState; + char* szCurrentState; + char* rgbAtr; + LPSCARD_READERSTATEA readerState; + + if (!WLog_IsLevelActive(smartcard->log, WLOG_DEBUG)) + return; + + WLog_Print(smartcard->log, WLOG_DEBUG, "LocateCardsByATRA_Call {"); + + pb = (BYTE*) &(call->hContext.pbContext); + + if (call->hContext.cbContext > 4) + { + WLog_Print(smartcard->log, WLOG_DEBUG, "hContext: 0x%02X%02X%02X%02X%02X%02X%02X%02X (%d)", + pb[0], pb[1], pb[2], pb[3], pb[4], pb[5], pb[6], pb[7], call->hContext.cbContext); + } + else + { + WLog_Print(smartcard->log, WLOG_DEBUG, "hContext: 0x%02X%02X%02X%02X (%d)", + pb[0], pb[1], pb[2], pb[3], call->hContext.cbContext); + } + + for (index = 0; index < call->cReaders; index++) + { + readerState = (LPSCARD_READERSTATEA) &call->rgReaderStates[index]; + + WLog_Print(smartcard->log, WLOG_DEBUG, + "\t[%d]: szReader: %s cbAtr: %d", + index, readerState->szReader, readerState->cbAtr); + + szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState); + szEventState = SCardGetReaderStateString(readerState->dwEventState); + rgbAtr = winpr_BinToHexString((BYTE*) &(readerState->rgbAtr), readerState->cbAtr, FALSE); + + WLog_Print(smartcard->log, WLOG_DEBUG, + "\t[%d]: dwCurrentState: %s (0x%08X)", + index, szCurrentState, readerState->dwCurrentState); + + WLog_Print(smartcard->log, WLOG_DEBUG, + "\t[%d]: dwEventState: %s (0x%08X)", + index, szEventState, readerState->dwEventState); + + if (rgbAtr) + { + WLog_Print(smartcard->log, WLOG_DEBUG, + "\t[%d]: cbAtr: %d rgbAtr: %s", + index, readerState->cbAtr, rgbAtr); + } + else + { + WLog_Print(smartcard->log, WLOG_DEBUG, + "\t[%d]: cbAtr: %d rgbAtr: %s", + index, 0, ""); + } + + free(szCurrentState); + free(szEventState); + free(rgbAtr); + } + WLog_Print(smartcard->log, WLOG_DEBUG, "}"); +} diff --git a/channels/smartcard/client/smartcard_pack.h b/channels/smartcard/client/smartcard_pack.h index 788c75795..5c5324bb5 100644 --- a/channels/smartcard/client/smartcard_pack.h +++ b/channels/smartcard/client/smartcard_pack.h @@ -532,4 +532,8 @@ void smartcard_trace_transmit_call(SMARTCARD_DEVICE* smartcard, Transmit_Call* c UINT32 smartcard_pack_transmit_return(SMARTCARD_DEVICE* smartcard, wStream* s, Transmit_Return* ret); void smartcard_trace_transmit_return(SMARTCARD_DEVICE* smartcard, Transmit_Return* ret); +UINT32 smartcard_unpack_locate_cards_by_atr_a_call(SMARTCARD_DEVICE* smartcard, wStream* s, LocateCardsByATRA_Call* call); +void smartcard_trace_locate_cards_by_atr_a_call(SMARTCARD_DEVICE* smartcard, LocateCardsByATRA_Call* call); + + #endif /* FREERDP_CHANNEL_SMARTCARD_CLIENT_PACK_H */ diff --git a/winpr/libwinpr/smartcard/smartcard_pcsc.c b/winpr/libwinpr/smartcard/smartcard_pcsc.c index d9878cf76..ebf4420be 100644 --- a/winpr/libwinpr/smartcard/smartcard_pcsc.c +++ b/winpr/libwinpr/smartcard/smartcard_pcsc.c @@ -1382,9 +1382,7 @@ WINSCARDAPI LONG WINAPI PCSC_SCardGetStatusChange_Internal(SCARDCONTEXT hContext { int i, j; int* map; - DWORD dwEventState; PCSC_DWORD cMappedReaders; - BOOL stateChanged = FALSE; PCSC_SCARD_READERSTATE* states; LONG status = SCARD_S_SUCCESS; PCSC_DWORD pcsc_dwTimeout = (PCSC_DWORD) dwTimeout; @@ -1437,6 +1435,7 @@ WINSCARDAPI LONG WINAPI PCSC_SCardGetStatusChange_Internal(SCARDCONTEXT hContext states[j].szReader = rgReaderStates[i].szReader; states[j].dwCurrentState = rgReaderStates[i].dwCurrentState; + states[j].pvUserData = rgReaderStates[i].pvUserData; states[j].dwEventState = rgReaderStates[i].dwEventState; states[j].cbAtr = rgReaderStates[i].cbAtr; CopyMemory(&(states[j].rgbAtr), &(rgReaderStates[i].rgbAtr), PCSC_MAX_ATR_SIZE); @@ -1470,6 +1469,11 @@ WINSCARDAPI LONG WINAPI PCSC_SCardGetStatusChange_Internal(SCARDCONTEXT hContext rgReaderStates[i].dwCurrentState = states[j].dwCurrentState; rgReaderStates[i].cbAtr = states[j].cbAtr; CopyMemory(&(rgReaderStates[i].rgbAtr), &(states[j].rgbAtr), PCSC_MAX_ATR_SIZE); + /** + * Why we should interpret and modify the results of pcsc-lite ScardGetStatusChange ? + * Should not we just act as a pass-through between the client and the remote smartcard subsystem ? + */ +#if 0 /* pcsc-lite puts an event count in the higher bits of dwEventState */ states[j].dwEventState &= 0xFFFF; dwEventState = states[j].dwEventState & ~SCARD_STATE_CHANGED; @@ -1493,12 +1497,19 @@ WINSCARDAPI LONG WINAPI PCSC_SCardGetStatusChange_Internal(SCARDCONTEXT hContext if (rgReaderStates[i].dwCurrentState & SCARD_STATE_IGNORE) rgReaderStates[i].dwEventState = SCARD_STATE_IGNORE; +#endif + rgReaderStates[i].dwEventState = states[j].dwEventState; } - + /** + * Why we should interpret and modify the results of pcsc-lite ScardGetStatusChange ? + * Should not we just act as a pass-through between the client and the remote smartcard subsystem ? + */ +#if 0 if ((status == SCARD_S_SUCCESS) && !stateChanged) status = SCARD_E_TIMEOUT; else if ((status == SCARD_E_TIMEOUT) && stateChanged) return SCARD_S_SUCCESS; +#endif free(states); free(map); @@ -1610,7 +1621,15 @@ WINSCARDAPI LONG WINAPI PCSC_SCardConnect_Internal(SCARDCONTEXT hContext, if (!szReaderPCSC) szReaderPCSC = (char*) szReader; - pcsc_dwPreferredProtocols = (PCSC_DWORD) PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols); + /** + * As stated here : https://pcsclite.alioth.debian.org/api/group__API.html#ga4e515829752e0a8dbc4d630696a8d6a5 + * SCARD_PROTOCOL_UNDEFINED is valid for dwPreferredProtocols (only) if dwShareMode == SCARD_SHARE_DIRECT + * and allows to send control commands to the reader (with SCardControl()) even if a card is not present in the reader + */ + if (pcsc_dwShareMode == SCARD_SHARE_DIRECT && dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) + pcsc_dwPreferredProtocols = SCARD_PROTOCOL_UNDEFINED; + else + pcsc_dwPreferredProtocols = (PCSC_DWORD) PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols); status = (LONG) g_PCSC.pfnSCardConnect(hPrivateContext, szReaderPCSC, pcsc_dwShareMode, pcsc_dwPreferredProtocols, phCard, &pcsc_dwActiveProtocol); status = PCSC_MapErrorCodeToWinSCard(status); @@ -2107,11 +2126,7 @@ WINSCARDAPI LONG WINAPI PCSC_SCardControl(SCARDHANDLE hCard, { ioCtlValue = _byteswap_ulong(tlv->value); ioCtlValue -= 0x42000000; /* inverse of PCSC_SCARD_CTL_CODE() */ - IoCtlMethod = METHOD_FROM_CTL_CODE(ioCtlValue); - IoCtlFunction = FUNCTION_FROM_CTL_CODE(ioCtlValue); - IoCtlAccess = ACCESS_FROM_CTL_CODE(ioCtlValue); - IoCtlDeviceType = DEVICE_TYPE_FROM_CTL_CODE(ioCtlValue); - ioCtlValue = SCARD_CTL_CODE(IoCtlFunction); + ioCtlValue = SCARD_CTL_CODE(ioCtlValue); tlv->value = _byteswap_ulong(ioCtlValue); } } diff --git a/winpr/libwinpr/smartcard/smartcard_pcsc.h b/winpr/libwinpr/smartcard/smartcard_pcsc.h index 7c83e2a3c..a25dd64be 100644 --- a/winpr/libwinpr/smartcard/smartcard_pcsc.h +++ b/winpr/libwinpr/smartcard/smartcard_pcsc.h @@ -78,7 +78,14 @@ typedef long PCSC_LONG; #define PCSC_SCARD_CTL_CODE(code) (0x42000000 + (code)) #define PCSC_CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400) +/** + * pcsc-lite defines SCARD_READERSTATE, SCARD_IO_REQUEST as packed + * on Mac OS X only and uses default packing everywhere else. + */ + +#ifdef __APPLE__ #pragma pack(push, 1) +#endif typedef struct { @@ -97,6 +104,12 @@ typedef struct PCSC_DWORD cbPciLength; } PCSC_SCARD_IO_REQUEST; +#ifdef __APPLE__ +#pragma pack(pop) +#endif + +#pragma pack(push, 1) + typedef struct { BYTE tag;