diff options
Diffstat (limited to 'nfc/Simulator/Src/Queue.cpp')
| -rw-r--r-- | nfc/Simulator/Src/Queue.cpp | 2420 |
1 files changed, 2420 insertions, 0 deletions
diff --git a/nfc/Simulator/Src/Queue.cpp b/nfc/Simulator/Src/Queue.cpp new file mode 100644 index 00000000..c72dfba9 --- /dev/null +++ b/nfc/Simulator/Src/Queue.cpp @@ -0,0 +1,2420 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs the ioctl operations. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "Queue.tmh" + +CQueue::CQueue(WDFQUEUE Queue) : + m_Queue(Queue), + m_SmartCardReader(WdfIoQueueGetDevice(Queue)), + m_SocketListener(this), + m_pSEManager(nullptr), + m_pHCEConnection(nullptr), + m_NfpRadioState(TRUE), + m_NfpRadioOffPolicyOverride(FALSE), + m_NfpRadioOffSystemOverride(FALSE), + m_SERadioState(TRUE), + m_NfpInterfaceCreated(FALSE), + m_ScInterfaceCreated(FALSE), + m_SEInterfaceCreated(FALSE), + m_RoutingTable(this), + m_HCEConnectionId(0) +{ + NT_ASSERT(m_Queue != nullptr); + + InitializeListHead(&m_SubsList); + InitializeListHead(&m_ArrivalSubsList); + InitializeListHead(&m_DepartureSubsList); + InitializeListHead(&m_PubsList); + InitializeListHead(&m_ConnectionList); + InitializeListHead(&m_SecureElementList); + InitializeListHead(&m_SEEventsList); + + InitializeCriticalSection(&m_SubsLock); + InitializeCriticalSection(&m_PubsLock); + InitializeCriticalSection(&m_ConnectionLock); + InitializeCriticalSection(&m_SEManagerLock); + InitializeCriticalSection(&m_SEEventsLock); + InitializeCriticalSection(&m_RadioLock); + InitializeCriticalSection(&m_pHCEConnectionLock); +} + +CQueue::~CQueue() +{ + DeleteCriticalSection(&m_SubsLock); + DeleteCriticalSection(&m_PubsLock); + DeleteCriticalSection(&m_ConnectionLock); + DeleteCriticalSection(&m_SEEventsLock); + DeleteCriticalSection(&m_RadioLock); + DeleteCriticalSection(&m_pHCEConnectionLock); + + m_Queue = nullptr; +} + +NTSTATUS CQueue::Initialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WSADATA WsaData = {}; + + DECLARE_CONST_UNICODE_STRING(RMNamespace, RM_NAMESPACE); + DECLARE_CONST_UNICODE_STRING(SCNamespace, SMARTCARD_READER_NAMESPACE); + DECLARE_CONST_UNICODE_STRING(SimulatorNamespace, SIM_NAMESPACE); + + ReadSettingsFromRegistry(); + + if (WSAStartup(MAKEWORD(2,2), &WsaData) != 0) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + + if (!NT_SUCCESS(m_SmartCardReader.Initialize())) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + if (FAILED(m_SocketListener.Bind())) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + if (FAILED(m_SocketListener.EnableAccepting())) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + EnumerateSecureElements(); + m_RoutingTable.Initialize(WdfIoQueueGetDevice(m_Queue)); + + // Register for NFP Radio Manager interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_NFC_RADIO_MEDIA_DEVICE_INTERFACE, + &RMNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with %!STATUS!", Status); + goto Exit; + } + + if (IsNfpRadioEnabled()) { + // Register for Proximity interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_NfpInterfaceCreated = TRUE; + + // Register for Smart Card interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_ScInterfaceCreated = TRUE; + } + + if (IsSERadioEnabled()) { + // Register for Secure Element interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFCSE, + nullptr); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_SEInterfaceCreated = TRUE; + } + + // Register for Simulator interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFCSIM, + &SimulatorNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with %!STATUS!", Status); + goto Exit; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CQueue::Deinitialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + while (!IsListEmpty(&m_SecureElementList)) { + delete CSecureElement::FromListEntry(RemoveHeadList(&m_SecureElementList)); + } + + + m_SocketListener.StopAccepting(); + m_SmartCardReader.Deinitialize(); + + while (!IsListEmpty(&m_ConnectionList)) { + delete CConnection::FromListEntry(RemoveHeadList(&m_ConnectionList)); + } + + WSACleanup(); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +BOOLEAN CQueue::IsNfpRadioEnabled() +{ + BOOLEAN NfpRadioState; + + EnterCriticalSection(&m_RadioLock); + NfpRadioState = m_NfpRadioState; + LeaveCriticalSection(&m_RadioLock); + + return NfpRadioState; +} + +BOOLEAN CQueue::IsSERadioEnabled() +{ + BOOLEAN SERadioState; + + EnterCriticalSection(&m_RadioLock); + SERadioState = m_SERadioState; + LeaveCriticalSection(&m_RadioLock); + + return SERadioState; +} + +NTSTATUS CQueue::DetectRole(CFileObject *pFileObject, PCWSTR pszFileName) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCWSTR pszProtocol = nullptr; + PCWSTR pszType = nullptr; + + MethodEntry("pszFileName = %S", pszFileName); + + if (IsStringPrefixed(pszFileName, PUBS_NAMESPACE)) { + if (IsNfpRadioEnabled()) { + pFileObject->SetRolePublication(); + pszProtocol = pszFileName + PUBS_NAMESPACE_CHARS; + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringPrefixed(pszFileName, SUBS_NAMESPACE)) { + if (IsNfpRadioEnabled()) { + pFileObject->SetRoleSubcription(); + pszProtocol = pszFileName + SUBS_NAMESPACE_CHARS; + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SEEVENTS_NAMESPACE)) { + if (IsSERadioEnabled()) { + pFileObject->SetRoleSecureElementEvent(); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SEMANAGE_NAMESPACE)) { + if (IsSERadioEnabled()) { + pFileObject->SetRoleSecureElementManager(); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SMARTCARD_READER_NAMESPACE)) { + if (IsNfpRadioEnabled()) { + pFileObject->SetRoleSmartCardReader(); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SIM_NAMESPACE)) { + pFileObject->SetRoleSimulation(); + } + else if (IsStringEqual(pszFileName, RM_NAMESPACE)) { + pFileObject->SetRoleRadioManager(); + } + else { + Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + + if (NT_SUCCESS(Status) && pszProtocol != nullptr) { + Status = STATUS_OBJECT_PATH_NOT_FOUND; + + if (IsStringPrefixed(pszProtocol, WINDOWS_PROTOCOL)) { + pszType = pszProtocol + WINDOWS_PROTOCOL_CHARS; + + if (*pszType == L'.') { + Status = pFileObject->SetType(pszProtocol); + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication() && IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (IsStringPrefixed(pszType, WINDOWSURI_TYPE)) { + pszType = pszType + WINDOWSURI_TYPE_CHARS; + + if (*pszType == L'\0') { + Status = pFileObject->SetType(pszProtocol); + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication() && IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + } + else if (IsStringPrefixed(pszType, WINDOWSMIME_TYPE)) { + pszType = pszType + WINDOWSMIME_TYPE_CHARS; + + if (*pszType == L'\0') { + if (pFileObject->IsNormalSubscription()) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication() && IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (*pszType == L'.') { + Status = pFileObject->SetType(pszProtocol); + } + } + } + else if (IsStringPrefixed(pszProtocol, NDEF_PROTOCOL)) { + pszType = pszProtocol + NDEF_PROTOCOL_CHARS; + + if (*pszType == L'\0') { + Status = pFileObject->SetType(pszProtocol); + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication()) { + if (IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else { + if (IsStringPrefixed(pszType, NDEF_EXT_TYPE) || + IsStringPrefixed(pszType, NDEF_MIME_TYPE) || + IsStringPrefixed(pszType, NDEF_URI_TYPE) || + IsStringPrefixed(pszType, NDEF_WKT_TYPE) || + IsStringPrefixed(pszType, NDEF_UNKNOWN_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + } + } + else if (pFileObject->IsPublication()) { + if (IsStringEqual(pszProtocol, LAUNCHAPP_WRITETAG_PROTOCOL)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (pFileObject->IsNormalSubscription()) { + if (IsStringEqual(pszProtocol, DEVICE_ARRIVED)) { + if (pFileObject->SetRoleArrivedSubcription()) { + Status = STATUS_SUCCESS; + } + } + else if (IsStringEqual(pszProtocol, DEVICE_DEPARTED)) { + if (pFileObject->SetRoleDepartedSubcription()) { + Status = STATUS_SUCCESS; + } + } + else if (IsStringEqual(pszProtocol, PAIRING_BLUETOOTH_PROTOCOL) || + IsStringEqual(pszProtocol, PAIRING_UPNP_PROTOCOL) || + IsStringEqual(pszProtocol, WRITEABLETAG_PROTOCOL)) { + Status = pFileObject->SetType(pszProtocol); + } + } + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +void +CQueue::OnFileCreate( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _In_ WDFFILEOBJECT FileObject + ) +{ + MethodEntry("Request = %p, FileObject = %p", Request, FileObject); + + NTSTATUS Status = STATUS_SUCCESS; + PUNICODE_STRING FileName = nullptr; + PCWSTR pszFileName = nullptr; + CFileObject *pFileObject = nullptr; + + UNREFERENCED_PARAMETER(Device); + + // Construct file object on the preallocated buffer using placement 'new' + pFileObject = new (GetFileObject(FileObject)) CFileObject(FileObject); + + if (pFileObject == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + FileName = WdfFileObjectGetFileName(FileObject); + + if ((FileName != nullptr) && (FileName->Length > 0)) { + NT_ASSERT(FileName->Buffer != nullptr); + + if ((FileName->Length / sizeof(WCHAR)) > MaxCchType) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pszFileName = FileName->Buffer; + pszFileName = (pszFileName[0] == L'\\') ? pszFileName+1 : pszFileName; + + Status = DetectRole(pFileObject, pszFileName); + + if (!NT_SUCCESS(Status)) { + goto Exit; + } + + TraceInfo("pszFileName = %S pFileObject = %p", pszFileName, pFileObject); + } + + if (pFileObject->IsNormalSubscription()) { + EnterCriticalSection(&m_SubsLock); + InsertHeadList(&m_SubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SubsLock); + } + else if (pFileObject->IsArrivedSubscription()) { + EnterCriticalSection(&m_SubsLock); + InsertHeadList(&m_ArrivalSubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SubsLock); + } + else if (pFileObject->IsDepartedSubscription()) { + EnterCriticalSection(&m_SubsLock); + InsertHeadList(&m_DepartureSubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SubsLock); + } + else if (pFileObject->IsSmartCardReader()) { + Status = m_SmartCardReader.AddClient(pFileObject); + } + else if (pFileObject->IsSecureElementManager()) { + EnterCriticalSection(&m_SEManagerLock); + Status = InterlockedCompareExchangePointer((void**)&m_pSEManager, pFileObject, nullptr) == nullptr ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; + LeaveCriticalSection(&m_SEManagerLock); + } + +Exit: + TraceInfo("%!FUNC! Completing Request with Status %!STATUS!", Status); + WdfRequestComplete(Request, Status); + + MethodReturnVoid(); +} + +void CQueue::OnFileClose(_In_ WDFFILEOBJECT FileObject) +{ + MethodEntry("..."); + + CFileObject* pFileObject = GetFileObject(FileObject); + CRITICAL_SECTION *pLock = nullptr; + LIST_ENTRY* pHead = nullptr; + + NT_ASSERT(pFileObject != nullptr); + + if (pFileObject->IsNormalSubscription()) { + pLock = &m_SubsLock; + pHead = &m_SubsList; + } + else if (pFileObject->IsArrivedSubscription()) { + pLock = &m_SubsLock; + pHead = &m_ArrivalSubsList; + } + else if (pFileObject->IsDepartedSubscription()) { + pLock = &m_SubsLock; + pHead = &m_DepartureSubsList; + } + else if (pFileObject->IsPublication()) { + pLock = &m_PubsLock; + pHead = &m_PubsList; + } + else if (pFileObject->IsSmartCardReader()) { + m_SmartCardReader.RemoveClient(pFileObject); + } + else if (pFileObject->IsSecureElementEvent()) { + pLock = &m_SEEventsLock; + pHead = &m_SEEventsList; + } + else if (pFileObject->IsSecureElementManager()) { + EnterCriticalSection(&m_SEManagerLock); + InterlockedCompareExchangePointer((void**)&m_pSEManager, nullptr, pFileObject); + LeaveCriticalSection(&m_SEManagerLock); + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList; + pEntry = pEntry->Flink) { + CSecureElement::FromListEntry(pEntry)->SetEmulationMode(EmulationOff); + } + } + + if (pHead != nullptr) { + EnterCriticalSection(pLock); + + for (LIST_ENTRY* pEntry = pHead->Flink; + pEntry != pHead; + pEntry = pEntry->Flink) { + if (pEntry == pFileObject->GetListEntry()) { + RemoveEntryList(pEntry); + break; + } + } + LeaveCriticalSection(pLock); + } + + MethodReturnVoid(); +} + +VOID +CQueue::OnIoDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +{ + FunctionEntry("..."); + + CQueue *pQueue = GetQueueObject(Queue); + + NT_ASSERT(pQueue != nullptr); + pQueue->OnIoDeviceControl(Request, IoControlCode, InputBufferLength, OutputBufferLength); + + FunctionReturnVoid(); +} + +void +CQueue::OnIoDeviceControl( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ size_t InputBufferLength, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("Request = %p, IoControlCode = 0x%x InputBufferLength=%d OutputBufferLength=%d", + Request, IoControlCode, (DWORD)InputBufferLength, (DWORD)OutputBufferLength); + + NTSTATUS Status = STATUS_INVALID_DEVICE_STATE; + + switch (IoControlCode) + { + case IOCTL_NFP_GET_MAX_MESSAGE_BYTES: + case IOCTL_NFP_GET_KILO_BYTES_PER_SECOND: + case IOCTL_NFP_DISABLE: + case IOCTL_NFP_ENABLE: + case IOCTL_NFP_SET_PAYLOAD: + case IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE: + case IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE: + case IOCTL_NFCSIM_BEGIN_PROXIMITY: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_NFP_ENABLE, 0, 0, &CQueue::OnNfpEnable }, + { IOCTL_NFP_DISABLE, 0, 0, &CQueue::OnNfpDisable }, + { IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE, 0, sizeof(DWORD), &CQueue::OnNfpGetNextSubscribedMessage }, + { IOCTL_NFP_SET_PAYLOAD, MinCbPayload, 0, &CQueue::OnNfpSetPayload }, + { IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE, 0, 0, &CQueue::OnNfpGetNextTransmittedMessage }, + { IOCTL_NFP_GET_MAX_MESSAGE_BYTES, 0, sizeof(DWORD), &CQueue::OnNfpGetMaxMessageBytes }, + { IOCTL_NFP_GET_KILO_BYTES_PER_SECOND, 0, sizeof(DWORD), &CQueue::OnNfpGetTransmissionRateKbps }, + { IOCTL_NFCSIM_BEGIN_PROXIMITY, sizeof(BEGIN_PROXIMITY_ARGS), 0, &CQueue::OnNfpBeginProximity }, + }; + + if (IsNfpRadioEnabled()) { + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + } + break; + + case IOCTL_NFCSE_ENUM_ENDPOINTS: + case IOCTL_NFCSE_SUBSCRIBE_FOR_EVENT: + case IOCTL_NFCSE_GET_NEXT_EVENT: + case IOCTL_NFCSE_SET_CARD_EMULATION_MODE: + case IOCTL_NFCSIM_TRIGGER_SEEVENT: + case IOCTL_NFCSE_GET_NFCC_CAPABILITIES: + case IOCTL_NFCSE_GET_ROUTING_TABLE: + case IOCTL_NFCSE_SET_ROUTING_TABLE: + case IOCTL_NFCSE_HCE_REMOTE_RECV: + case IOCTL_NFCSE_HCE_REMOTE_SEND: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_NFCSE_SET_CARD_EMULATION_MODE, sizeof(SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO), 0, &CQueue::OnSESetCardEmulationMode }, + { IOCTL_NFCSE_GET_NEXT_EVENT, 0, sizeof(DWORD), &CQueue::OnSEGetNextEvent }, + { IOCTL_NFCSE_SUBSCRIBE_FOR_EVENT, sizeof(SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO), 0, &CQueue::OnSESubscribeForEvent }, + { IOCTL_NFCSE_ENUM_ENDPOINTS, 0, sizeof(DWORD), &CQueue::OnSEEnumEndpoints }, + { IOCTL_NFCSE_GET_NFCC_CAPABILITIES, 0, sizeof(SECURE_ELEMENT_NFCC_CAPABILITIES), &CQueue::OnSEGetNfccCapabilities }, + { IOCTL_NFCSE_GET_ROUTING_TABLE, 0, sizeof(DWORD), &CQueue::OnSEGetRoutingTable }, + { IOCTL_NFCSE_SET_ROUTING_TABLE, sizeof(SECURE_ELEMENT_ROUTING_TABLE), 0, &CQueue::OnSESetRoutingTable }, + { IOCTL_NFCSE_HCE_REMOTE_RECV, 0, sizeof(DWORD), &CQueue::OnSEHCERemoteRecv }, + { IOCTL_NFCSE_HCE_REMOTE_SEND, 2 * sizeof(USHORT) + 2, 0, &CQueue::OnSEHCERemoteSend }, + { IOCTL_NFCSIM_TRIGGER_SEEVENT, SECURE_ELEMENT_EVENT_INFO_HEADER, 0, &CQueue::OnSETriggerEvent }, + }; + + if (IsSERadioEnabled()) { + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + } + break; + + case IOCTL_SMARTCARD_EJECT: + case IOCTL_SMARTCARD_GET_LAST_ERROR: + case IOCTL_SMARTCARD_SET_ATTRIBUTE: + case IOCTL_SMARTCARD_SWALLOW: + case IOCTL_SMARTCARD_GET_ATTRIBUTE: + case IOCTL_SMARTCARD_GET_STATE: + case IOCTL_SMARTCARD_IS_ABSENT: + case IOCTL_SMARTCARD_IS_PRESENT: + case IOCTL_SMARTCARD_POWER: + case IOCTL_SMARTCARD_SET_PROTOCOL: + case IOCTL_SMARTCARD_TRANSMIT: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_SMARTCARD_GET_ATTRIBUTE, sizeof(DWORD), 0, &CQueue::OnSCGetAttribute }, + { IOCTL_SMARTCARD_SET_ATTRIBUTE, sizeof(DWORD), 0, &CQueue::OnSCSetAttribute }, + { IOCTL_SMARTCARD_GET_STATE, 0, sizeof(DWORD), &CQueue::OnSCGetState }, + { IOCTL_SMARTCARD_POWER, sizeof(DWORD), 0, &CQueue::OnSCPower }, + { IOCTL_SMARTCARD_SET_PROTOCOL, sizeof(DWORD), sizeof(DWORD), &CQueue::OnSCSetProtocol }, + { IOCTL_SMARTCARD_IS_ABSENT, 0, 0, &CQueue::OnSCIsAbsent }, + { IOCTL_SMARTCARD_IS_PRESENT, 0, 0, &CQueue::OnSCIsPresent }, + { IOCTL_SMARTCARD_TRANSMIT, sizeof(SCARD_IO_REQUEST)+1, sizeof(SCARD_IO_REQUEST)+2, &CQueue::OnSCTransmit }, + { IOCTL_SMARTCARD_GET_LAST_ERROR, 0, 0, &CQueue::OnSCGetLastError }, + }; + + if (IsNfpRadioEnabled()) { + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + } + break; + + case IOCTL_NFCRM_SET_RADIO_STATE: + case IOCTL_NFCRM_QUERY_RADIO_STATE: + case IOCTL_NFCSERM_SET_RADIO_STATE: + case IOCTL_NFCSERM_QUERY_RADIO_STATE: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_NFCRM_SET_RADIO_STATE, sizeof(NFCRM_SET_RADIO_STATE), 0, &CQueue::OnNfpSetRadioState }, + { IOCTL_NFCRM_QUERY_RADIO_STATE, 0, sizeof(NFCRM_RADIO_STATE), &CQueue::OnNfpQueryRadioState }, + }; + + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + break; + + default: + { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } + } + + if (Status != STATUS_PENDING) { + TraceInfo("%!FUNC! Completing Request with Status %!STATUS!", Status); + WdfRequestComplete(Request, Status); + } + + MethodReturnVoid(); +} + +BOOL CQueue::ValidateMessage(_In_ CFileObject *pFileObject, _In_ ULONG IoControlCode) +{ + switch (IoControlCode) + { + case IOCTL_NFP_GET_MAX_MESSAGE_BYTES: + case IOCTL_NFP_GET_KILO_BYTES_PER_SECOND: + { + return TRUE; + } + + case IOCTL_NFP_DISABLE: + case IOCTL_NFP_ENABLE: + { + return (pFileObject->IsPublication() || pFileObject->IsSubscription()); + } + + case IOCTL_NFP_SET_PAYLOAD: + case IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE: + { + return pFileObject->IsPublication(); + } + + case IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE: + { + return pFileObject->IsSubscription(); + } + + case IOCTL_NFCSE_GET_NFCC_CAPABILITIES: + case IOCTL_NFCSE_ENUM_ENDPOINTS: + { + return TRUE; + } + + case IOCTL_NFCSE_GET_NEXT_EVENT: + case IOCTL_NFCSE_SUBSCRIBE_FOR_EVENT: + { + return pFileObject->IsSecureElementEvent(); + } + + case IOCTL_NFCSE_GET_ROUTING_TABLE: + case IOCTL_NFCSE_SET_ROUTING_TABLE: + case IOCTL_NFCSE_HCE_REMOTE_SEND: + case IOCTL_NFCSE_HCE_REMOTE_RECV: + case IOCTL_NFCSE_SET_CARD_EMULATION_MODE: + { + return pFileObject->IsSecureElementManager(); + } + + case IOCTL_SMARTCARD_EJECT: + case IOCTL_SMARTCARD_GET_LAST_ERROR: + case IOCTL_SMARTCARD_SET_ATTRIBUTE: + case IOCTL_SMARTCARD_SWALLOW: + case IOCTL_SMARTCARD_GET_ATTRIBUTE: + case IOCTL_SMARTCARD_GET_STATE: + case IOCTL_SMARTCARD_IS_ABSENT: + case IOCTL_SMARTCARD_IS_PRESENT: + case IOCTL_SMARTCARD_POWER: + case IOCTL_SMARTCARD_SET_PROTOCOL: + case IOCTL_SMARTCARD_TRANSMIT: + { + return pFileObject->IsSmartCardReader(); + } + + case IOCTL_NFCRM_SET_RADIO_STATE: + case IOCTL_NFCRM_QUERY_RADIO_STATE: + { + return pFileObject->IsRoleRadioManager(); + } + + case IOCTL_NFCSIM_BEGIN_PROXIMITY: + case IOCTL_NFCSIM_TRIGGER_SEEVENT: + { + return pFileObject->IsRoleSimulation(); + } + } + + return FALSE; +} + +NTSTATUS +CQueue::DispatchMessage( + _In_reads_(TableEntries) const DISPATCH_ENTRY rgDispatchTable[], + _In_ DWORD TableEntries, + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ size_t InputBufferLength, + _In_ size_t OutputBufferLength + ) +{ + NTSTATUS Status = STATUS_NOT_SUPPORTED; + PVOID InputBuffer = nullptr; + PVOID OutputBuffer = nullptr; + CFileObject *pFileObject = GetFileObject(WdfRequestGetFileObject(Request)); + + for (DWORD TableEntry = 0; TableEntry < TableEntries; TableEntry++) { + if (rgDispatchTable[TableEntry].IoControlCode != IoControlCode) + continue; + + if (InputBufferLength >= rgDispatchTable[TableEntry].MinInputBufferLength && + OutputBufferLength >= rgDispatchTable[TableEntry].MinOutputBufferLength) { + if (InputBufferLength != 0) { + Status = WdfRequestRetrieveInputBuffer(Request, 0, &InputBuffer, nullptr); + NT_ASSERT(NT_SUCCESS(Status)); + } + + if (OutputBufferLength != 0) { + Status = WdfRequestRetrieveOutputBuffer(Request, 0, &OutputBuffer, nullptr); + NT_ASSERT(NT_SUCCESS(Status)); + } + + if (ValidateMessage(pFileObject, IoControlCode)) { + Status = (this->*rgDispatchTable[TableEntry].DispatchHandler)( + pFileObject, + Request, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else { + Status = STATUS_INVALID_PARAMETER; + } + break; + } + + return Status; +} + +NTSTATUS +CQueue::OnNfpEnable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->Enable(); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpDisable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->Disable(); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpSetPayload( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (InputBufferLength > MaxCbPayload) { + Status = STATUS_INVALID_BUFFER_SIZE; + goto Exit; + } + + Status = pFileObject->SetPayload((DWORD)InputBufferLength, (PBYTE)InputBuffer); + + if (NT_SUCCESS(Status)) { + MESSAGE* pMessage = new MESSAGE(); + + if (pMessage != nullptr) { + pMessage->Initialize(pFileObject->GetType(), pFileObject->GetSize(), pFileObject->GetPayload()); + + EnterCriticalSection(&m_ConnectionLock); + + for (LIST_ENTRY* pEntry = m_ConnectionList.Flink; + pEntry != &m_ConnectionList; + pEntry = pEntry->Flink) { + CConnection* pConnection = CConnection::FromListEntry(pEntry); + + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) { + pFileObject->HandleMessageTransmitted(); + } + } + LeaveCriticalSection(&m_ConnectionLock); + + EnterCriticalSection(&m_PubsLock); + InsertHeadList(&m_PubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_PubsLock); + + delete pMessage; + } + else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetNextSubscribedMessage( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->GetNextSubscribedMessage(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetNextTransmittedMessage( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->GetNextTransmittedMessage(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetMaxMessageBytes( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pcbMaxPayload = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the output buffer has already been validated + // to contain 4 bytes, we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + *pcbMaxPayload = MaxCbPayload; + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetTransmissionRateKbps( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pdwKilobytesPerSecond = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the output buffer has already been validated + // to contain 4 bytes, we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + *pdwKilobytesPerSecond = KilobytesPerSecond; + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpBeginProximity( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BEGIN_PROXIMITY_ARGS *pArgs = (BEGIN_PROXIMITY_ARGS*)InputBuffer; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(BEGIN_PROXIMITY_ARGS) <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->BeginProximity(pArgs, this); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpSetRadioState( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + PNFCRM_SET_RADIO_STATE pRadioState = (PNFCRM_SET_RADIO_STATE)InputBuffer; + BOOLEAN NfpRadioState; + DECLARE_CONST_UNICODE_STRING(SCNamespace, SMARTCARD_READER_NAMESPACE); + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + _Analysis_assume_(sizeof(NFCRM_SET_RADIO_STATE) <= InputBufferLength); + + EnterCriticalSection(&m_RadioLock); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (pRadioState->SystemStateUpdate) { + m_NfpRadioOffSystemOverride = !pRadioState->MediaRadioOn; + } + else { + // + // Since the request is modifying the radio state independent of the + // system state, set the system state override to FALSE + // + m_NfpRadioOffSystemOverride = FALSE; + m_NfpRadioOffPolicyOverride = !pRadioState->MediaRadioOn; + } + + NfpRadioState = !m_NfpRadioOffSystemOverride && !m_NfpRadioOffPolicyOverride; + + if (NfpRadioState == m_NfpRadioState) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + if (NfpRadioState) { + if (!m_NfpInterfaceCreated) { + // Register for Proximity interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_NfpInterfaceCreated = TRUE; + } + + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr, + TRUE); + + if (!m_ScInterfaceCreated) { + // Register for Smart Card interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_ScInterfaceCreated = TRUE; + } + + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace, + TRUE); + } + else { + if (m_NfpInterfaceCreated) { + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr, + FALSE); + } + + if (m_ScInterfaceCreated) { + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace, + FALSE); + } + } + + m_NfpRadioState = NfpRadioState; + +Exit: + WriteSettingsToRegistry(); + LeaveCriticalSection(&m_RadioLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpQueryRadioState( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + PNFCRM_RADIO_STATE pRadioState = (PNFCRM_RADIO_STATE)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(NFCRM_RADIO_STATE) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pRadioState->MediaRadioOn = IsNfpRadioEnabled(); + WdfRequestCompleteWithInformation(Request, Status, sizeof(NFCRM_RADIO_STATE)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEEnumEndpoints( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + SECURE_ELEMENT_ENDPOINT_INFO rgEndpointInfo[MaxSecureElements] = {}; + SECURE_ELEMENT_ENDPOINT_LIST *pEndpointList = (SECURE_ELEMENT_ENDPOINT_LIST*)OutputBuffer; + DWORD cbOutputBuffer = 0; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + + // + // Since the output buffer has already been validated + // for output buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pEndpointList->NumberOfEndpoints = 0; + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList && pEndpointList->NumberOfEndpoints < _countof(rgEndpointInfo); + pEntry = pEntry->Flink) { + SECURE_ELEMENT_ENDPOINT_INFO Info; + + Info.guidSecureElementId = CSecureElement::FromListEntry(pEntry)->GetIdentifier(); + Info.eSecureElementType = CSecureElement::FromListEntry(pEntry)->GetType(); + + rgEndpointInfo[pEndpointList->NumberOfEndpoints++] = Info; + } + + cbOutputBuffer = sizeof(DWORD); + + if ((cbOutputBuffer + (pEndpointList->NumberOfEndpoints * sizeof(SECURE_ELEMENT_ENDPOINT_INFO))) <= OutputBufferLength) { + RtlCopyMemory(pEndpointList->EndpointList, rgEndpointInfo, pEndpointList->NumberOfEndpoints * sizeof(SECURE_ELEMENT_ENDPOINT_INFO)); + cbOutputBuffer += (pEndpointList->NumberOfEndpoints * sizeof(SECURE_ELEMENT_ENDPOINT_INFO)); + } + else { + // Returning this signals to the client to send a bigger buffer + Status = STATUS_BUFFER_OVERFLOW; + } + + WdfRequestCompleteWithInformation(Request, Status, cbOutputBuffer); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSESubscribeForEvent( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO *pInfo = (SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO*)InputBuffer; + CSecureElement *pSecureElement = nullptr; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO) <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (IsEqualGUID(pInfo->guidSecureElementId, GUID_NULL) || + NT_SUCCESS(Status = GetSecureElementObject(pInfo->guidSecureElementId, &pSecureElement))) { + Status = pFileObject->SubscribeForEvent(pInfo->guidSecureElementId, pInfo->eEventType); + + if (NT_SUCCESS(Status)) { + EnterCriticalSection(&m_SEEventsLock); + InsertHeadList(&m_SEEventsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SEEventsLock); + } + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEGetNextEvent( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->GetNextSecureElementPayload(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSESetCardEmulationMode( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO *pMode = (SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO*)InputBuffer; + CSecureElement *pSecureElement = nullptr; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO) <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = GetSecureElementObject(pMode->guidSecureElementId, &pSecureElement); + + if (NT_SUCCESS(Status)) { + Status = pSecureElement->SetEmulationMode(pMode->eMode); + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSETriggerEvent( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SECURE_ELEMENT_EVENT_INFO *pInfo = (SECURE_ELEMENT_EVENT_INFO*)InputBuffer; + CSecureElement *pSecureElement = nullptr; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(SECURE_ELEMENT_EVENT_INFO_HEADER <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (pInfo->guidSecureElementId == GUID_NULL) { + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList; + pEntry = pEntry->Flink) { + pInfo->guidSecureElementId = CSecureElement::FromListEntry(pEntry)->GetIdentifier(); + HandleSecureElementEvent(pInfo); + } + } + else { + Status = GetSecureElementObject(pInfo->guidSecureElementId, &pSecureElement); + + if (NT_SUCCESS(Status)) { + HandleSecureElementEvent(pInfo); + } + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEGetNfccCapabilities( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + PSECURE_ELEMENT_NFCC_CAPABILITIES pCapabilities = (PSECURE_ELEMENT_NFCC_CAPABILITIES)OutputBuffer; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_NFCC_CAPABILITIES) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pCapabilities->cbMaxRoutingTableSize = m_RoutingTable.MaxRoutingTableSize(); + pCapabilities->IsAidRoutingSupported = m_RoutingTable.IsAidRoutingSupported(); + pCapabilities->IsProtocolRoutingSupported = m_RoutingTable.IsProtocolRoutingSupported(); + pCapabilities->IsTechRoutingSupported = m_RoutingTable.IsTechRoutingSupported(); + + WdfRequestCompleteWithInformation(Request, Status, sizeof(SECURE_ELEMENT_NFCC_CAPABILITIES)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSESetRoutingTable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable = (PSECURE_ELEMENT_ROUTING_TABLE)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_ROUTING_TABLE) <= InputBufferLength); + + if (pRoutingTable->NumberOfEntries == 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if ((sizeof(SECURE_ELEMENT_ROUTING_TABLE) + (pRoutingTable->NumberOfEntries - 1) * sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY)) < InputBufferLength) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_RoutingTable.SetRoutingTable(pRoutingTable); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEGetRoutingTable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD cbOutputBuffer = 0; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBuffer); + + // + // Since the output buffer has already been validated + // for output buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_RoutingTable.GetRoutingTable(OutputBuffer, OutputBufferLength, &cbOutputBuffer); + WdfRequestCompleteWithInformation(Request, Status, cbOutputBuffer); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEHCERemoteRecv( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + GUID guidSecureElementId; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (!NT_SUCCESS(GetSecureElementId(DeviceHost, &guidSecureElementId))) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + Status = pFileObject->GetNextSecureElementPayload(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEHCERemoteSend( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + GUID guidSecureElementId; + MESSAGE* pMessage = nullptr; + PSECURE_ELEMENT_HCE_DATA_PACKET pDataPacket = (PSECURE_ELEMENT_HCE_DATA_PACKET)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(2 * sizeof(USHORT) + 2 <= InputBufferLength); + + EnterCriticalSection(&m_pHCEConnectionLock); + + if (OutputBufferLength != 0 || + pDataPacket->bConnectionId != m_HCEConnectionId) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (pDataPacket->cbPayload > MaxCbPayload) { + Status = STATUS_INVALID_BUFFER_SIZE; + goto Exit; + } + + if (!NT_SUCCESS(GetSecureElementId(DeviceHost, &guidSecureElementId))) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + if (m_pHCEConnection == nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + pMessage = new MESSAGE(); + + if (pMessage == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + pMessage->Initialize(HCE_MESSAGE_TYPE_TRANSMIT, pDataPacket->cbPayload, pDataPacket->pbPayload); + + if (FAILED(m_pHCEConnection->TransmitMessage(pMessage))) { + Status = STATUS_DATA_ERROR; + goto Exit; + } + +Exit: + SAFE_DELETE(pMessage); + LeaveCriticalSection(&m_pHCEConnectionLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCGetLastError( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + DWORD *pdwError = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + *pdwError = STATUS_SUCCESS; + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, sizeof(DWORD)); + + MethodReturn(STATUS_PENDING, "Status = %!STATUS!", STATUS_SUCCESS); +} + +NTSTATUS +CQueue::OnSCGetAttribute( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pdwAttributeId = (DWORD*)InputBuffer; + DWORD cbOutputBuffer = (DWORD)OutputBufferLength; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + Status = m_SmartCardReader.GetAttribute(*pdwAttributeId, (PBYTE)OutputBuffer, &cbOutputBuffer); + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(Request, Status, cbOutputBuffer); + fCompleteRequest = FALSE; + } + + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCSetAttribute( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD *pdwAttributeId = (DWORD*)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + + Status = m_SmartCardReader.SetAttribute(*pdwAttributeId); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCGetState( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD *pdwState = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + *pdwState = m_SmartCardReader.GetState(); + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + + MethodReturn(STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCIsAbsent( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_SmartCardReader.IsAbsent(Request); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCIsPresent( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_SmartCardReader.IsPresent(Request); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCPower( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD *pdwPower = (DWORD*)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + + Status = m_SmartCardReader.SetPower(*pdwPower); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCSetProtocol( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pdwProtocol = (DWORD*)InputBuffer; + DWORD *pdwSelectedProtocol = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + Status = m_SmartCardReader.SetProtocol(*pdwProtocol, pdwSelectedProtocol); + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + fCompleteRequest = FALSE; + } + + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCTransmit( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + size_t BytesTransferred = 0; + + UNREFERENCED_PARAMETER(pFileObject); + + // + // Since the input and buffer buffer length has already been validated + // for input buffer length we need to have atleast one byte to transfer (discarding the header) + // for output buffer length we should atleast be able to store status codes SW1+SW2 + // + _Analysis_assume_(sizeof(SCARD_IO_REQUEST)+1 <= InputBufferLength); + _Analysis_assume_(sizeof(SCARD_IO_REQUEST)+2 <= OutputBufferLength); + + Status = m_SmartCardReader.Transmit((PBYTE)InputBuffer, InputBufferLength, (PBYTE)OutputBuffer, OutputBufferLength, &BytesTransferred); + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, BytesTransferred); + fCompleteRequest = FALSE; + } + + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +void CQueue::ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) +{ + MethodEntry("..."); + + CConnection* pConnection; + + if (CConnection::Create(this, &pConnection)) { + // Mark it as an inbound connection, so we know to delete it when it's removed from the list + pConnection->SetInboundConnection(); + pConnection->ValidateAccept(Socket, pMagicPacket); + + Socket = INVALID_SOCKET; + } + + if (Socket != INVALID_SOCKET) { + closesocket(Socket); + } + + MethodReturnVoid(); +} + +void CQueue::ConnectionEstablished(_In_ CConnection* pConnection) +{ + MethodEntry("..."); + + if (pConnection->GetConnectionType() == CONNECTION_TYPE_P2P) { + EnterCriticalSection(&m_ConnectionLock); + + if (IsListEmpty(&m_ConnectionList)) { + HandleArrivalEvent(); + } + + InsertHeadList(&m_ConnectionList, pConnection->GetListEntry()); + LeaveCriticalSection(&m_ConnectionLock); + + MESSAGE* pMessage = new MESSAGE(); + + if (pMessage != nullptr) { + EnterCriticalSection(&m_PubsLock); + + for (LIST_ENTRY* pEntry = m_PubsList.Flink; + pEntry != &m_PubsList; + pEntry = pEntry->Flink) { + CFileObject* pPub = CFileObject::FromListEntry(pEntry); + + if (pPub->IsEnabled()) { + pMessage->Initialize(pPub->GetType(), pPub->GetSize(), pPub->GetPayload()); + + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) { + pPub->HandleMessageTransmitted(); + } + } + } + LeaveCriticalSection(&m_PubsLock); + + delete pMessage; + } + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_TAG) { + m_SmartCardReader.CardArrived(pConnection); + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_HCE) { + SECURE_ELEMENT_EVENT_INFO_AND_PAYLOAD(sizeof(SECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD)) EventInfo; + + if (NT_SUCCESS(GetSecureElementId(DeviceHost, &EventInfo.Info.guidSecureElementId))) { + EnterCriticalSection(&m_pHCEConnectionLock); + + if (m_pHCEConnection == nullptr) { + PSECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD pbEventPayload = NULL; + + m_pHCEConnection = pConnection; + m_HCEConnectionId++; + + EventInfo.Info.eEventType = HceActivated; + EventInfo.Info.cbEventData = sizeof(SECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD); + + pbEventPayload = (PSECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD)EventInfo.Info.pbEventData; + + pbEventPayload->bConnectionId = m_HCEConnectionId; + pbEventPayload->eRfTechType = NFC_RF_TECHNOLOGY_A; + pbEventPayload->eRfProtocolType = PROTOCOL_ISO_DEP; + + HandleSecureElementEvent(&EventInfo.Info); + } + + LeaveCriticalSection(&m_pHCEConnectionLock); + } + } + + MethodReturnVoid(); +} + +BOOL CQueue::ConnectionTerminated(_In_ CConnection* pConnection) +{ + MethodEntry("pConnection = 0x%p", pConnection); + + BOOL fConnectionDeleted = FALSE; + + if (pConnection->GetConnectionType() == CONNECTION_TYPE_P2P) { + LIST_ENTRY* pRemoveListEntry = pConnection->GetListEntry(); + + EnterCriticalSection(&m_ConnectionLock); + + for (LIST_ENTRY* pEntry = m_ConnectionList.Flink; + pEntry != &m_ConnectionList; + pEntry = pEntry->Flink) { + if (pEntry == pRemoveListEntry) { + RemoveEntryList(pEntry); + break; + } + } + + if (IsListEmpty(&m_ConnectionList)) { + HandleRemovalEvent(); + } + LeaveCriticalSection(&m_ConnectionLock); + + if (pConnection->IsInboundConnection()) { + delete pConnection; + fConnectionDeleted = TRUE; + } + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_TAG) { + fConnectionDeleted = m_SmartCardReader.CardRemoved(pConnection); + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_HCE) { + SECURE_ELEMENT_EVENT_INFO_AND_PAYLOAD(sizeof(USHORT)) EventInfo; + + if (NT_SUCCESS(GetSecureElementId(DeviceHost, &EventInfo.Info.guidSecureElementId))) { + EnterCriticalSection(&m_pHCEConnectionLock); + + if (m_pHCEConnection != nullptr) { + m_pHCEConnection = nullptr; + + EventInfo.Info.eEventType = HceDeactivated; + EventInfo.Info.cbEventData = sizeof(USHORT); + + *((USHORT*)EventInfo.Info.pbEventData) = m_HCEConnectionId; + + HandleSecureElementEvent(&EventInfo.Info); + } + LeaveCriticalSection(&m_pHCEConnectionLock); + } + } + + MethodReturnBool(fConnectionDeleted); +} + +void CQueue::HandleArrivalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_ArrivalSubsList.Flink; + pEntry != &m_ArrivalSubsList; + pEntry = pEntry->Flink) { + CFileObject::FromListEntry(pEntry)->HandleArrivalEvent(); + } + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +void CQueue::HandleRemovalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_DepartureSubsList.Flink; + pEntry != &m_DepartureSubsList; + pEntry = pEntry->Flink) { + CFileObject::FromListEntry(pEntry)->HandleRemovalEvent(); + } + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +void CQueue::HandleReceivedMessage(_In_ CONNECTION_TYPE ConnType, _In_ MESSAGE* pMessage) +{ + MethodEntry("pMessage->m_szType = '%S'", pMessage->m_szType); + + if (ConnType == CONNECTION_TYPE_P2P) { + if ((pMessage->m_cbPayload > 0) && (pMessage->m_cbPayload <= MaxCbPayload)) { + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_SubsList.Flink; pEntry != &m_SubsList; pEntry = pEntry->Flink) { + CFileObject* pSub = CFileObject::FromListEntry(pEntry); + pSub->HandleReceivedMessage(pMessage->m_szType, pMessage->m_cbPayload, pMessage->m_Payload); + } + LeaveCriticalSection(&m_SubsLock); + } + } + else if (ConnType == CONNECTION_TYPE_TAG) { + m_SmartCardReader.MessageReceived(pMessage); + } + else if (ConnType == CONNECTION_TYPE_HCE) { + EnterCriticalSection(&m_SEManagerLock); + + if (m_pSEManager != nullptr) { + m_pSEManager->HandleReceiveHcePacket((USHORT)m_HCEConnectionId, pMessage->m_cbPayload, pMessage->m_Payload); + } + LeaveCriticalSection(&m_SEManagerLock); + } + + MethodReturnVoid(); +} + +void CQueue::HandleSecureElementEvent(SECURE_ELEMENT_EVENT_INFO *pInfo) +{ + MethodEntry("..."); + + CSecureElement *pSecureElement = nullptr; + + if (NT_SUCCESS(GetSecureElementObject(pInfo->guidSecureElementId, &pSecureElement))) { + if (pSecureElement->GetEmulationMode() != EmulationOff) { + EnterCriticalSection(&m_SEEventsLock); + + for (LIST_ENTRY* pEntry = m_SEEventsList.Flink; + pEntry != &m_SEEventsList; + pEntry = pEntry->Flink) { + CFileObject::FromListEntry(pEntry)->HandleSecureElementEvent(pInfo); + } + LeaveCriticalSection(&m_SEEventsLock); + } + } + + MethodReturnVoid(); +} + +void CQueue::EnumerateSecureElements() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + WDFCOLLECTION Collection = nullptr; + GUID SecureElementId; + UNICODE_STRING GuidString; + CSecureElement *pSecureElement = nullptr; + + DECLARE_CONST_UNICODE_STRING(SEEndpointsKey, SE_ENDPOINTS_KEY); + + Status = WdfDeviceOpenRegistryKey( + WdfIoQueueGetDevice(m_Queue), + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfCollectionCreate(WDF_NO_OBJECT_ATTRIBUTES, &Collection); + + if (NT_SUCCESS(Status) && (Collection != nullptr)) { + Status = WdfRegistryQueryMultiString( + KeyHandle, + &SEEndpointsKey, + WDF_NO_OBJECT_ATTRIBUTES, + Collection); + + if (NT_SUCCESS(Status)) { + ULONG Count = WdfCollectionGetCount(Collection); + + for (ULONG Index = 0; Index < Count; Index++) { + WdfStringGetUnicodeString((WDFSTRING) WdfCollectionGetItem(Collection, Index), &GuidString); + + if (SUCCEEDED(CLSIDFromString(GuidString.Buffer, &SecureElementId)) && + (pSecureElement = new CSecureElement(SecureElementId, IsEqualGUID(SecureElementId, GUID_DH_SECURE_ELEMENT) ? DeviceHost : External)) != nullptr) { + TraceInfo("Secure Element Id=%S Type=%d", GuidString.Buffer, pSecureElement->GetType()); + InsertHeadList(&m_SecureElementList, pSecureElement->GetListEntry()); + } + } + } + + WdfObjectDelete(Collection); + } + + WdfRegistryClose(KeyHandle); + } + + MethodReturnVoid(); +} + +void CQueue::ReadSettingsFromRegistry() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + ULONG NfpRadioOffPolicyOverride = 0, NfpRadioOffSystemOverride = 0; + + DECLARE_CONST_UNICODE_STRING(NfpRadioTurnedOffKey, NFP_RADIO_TURNED_OFF_KEY); + DECLARE_CONST_UNICODE_STRING(NfpRadioFlightModeKey, NFP_RADIO_FLIGHT_MODE_KEY); + + Status = WdfDeviceOpenRegistryKey( + WdfIoQueueGetDevice(m_Queue), + PLUGPLAY_REGKEY_DEVICE | WDF_REGKEY_DEVICE_SUBKEY, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfRegistryQueryULong( + KeyHandle, + &NfpRadioTurnedOffKey, + &NfpRadioOffPolicyOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d", NFP_RADIO_TURNED_OFF_KEY, NfpRadioOffPolicyOverride); + m_NfpRadioOffPolicyOverride = (NfpRadioOffPolicyOverride != 0); + } + + Status = WdfRegistryQueryULong( + KeyHandle, + &NfpRadioFlightModeKey, + &NfpRadioOffSystemOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d", NFP_RADIO_FLIGHT_MODE_KEY, NfpRadioOffSystemOverride); + m_NfpRadioOffSystemOverride = (NfpRadioOffSystemOverride != 0); + } + + m_NfpRadioState = !m_NfpRadioOffPolicyOverride && !m_NfpRadioOffSystemOverride; + + WdfRegistryClose(KeyHandle); + } + + MethodReturnVoid(); +} + +void CQueue::WriteSettingsToRegistry() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + + DECLARE_CONST_UNICODE_STRING(NfpRadioTurnedOffKey, NFP_RADIO_TURNED_OFF_KEY); + DECLARE_CONST_UNICODE_STRING(NfpRadioFlightModeKey, NFP_RADIO_FLIGHT_MODE_KEY); + + Status = WdfDeviceOpenRegistryKey( + WdfIoQueueGetDevice(m_Queue), + PLUGPLAY_REGKEY_DEVICE | WDF_REGKEY_DEVICE_SUBKEY, + GENERIC_ALL & ~(GENERIC_WRITE | KEY_CREATE_SUB_KEY | WRITE_DAC), + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfRegistryAssignULong( + KeyHandle, + &NfpRadioTurnedOffKey, + m_NfpRadioOffPolicyOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d successfully persisted", NFP_RADIO_TURNED_OFF_KEY, m_NfpRadioOffPolicyOverride); + } + + Status = WdfRegistryAssignULong( + KeyHandle, + &NfpRadioFlightModeKey, + m_NfpRadioOffSystemOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d successfully persisted", NFP_RADIO_FLIGHT_MODE_KEY, m_NfpRadioOffSystemOverride); + } + + WdfRegistryClose(KeyHandle); + } + + MethodReturnVoid(); +} + +NTSTATUS CQueue::GetSecureElementObject(GUID &SecureElementId, CSecureElement **ppSecureElement) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList; + pEntry = pEntry->Flink) { + if (SecureElementId == CSecureElement::FromListEntry(pEntry)->GetIdentifier()) { + *ppSecureElement = CSecureElement::FromListEntry(pEntry); + Status = STATUS_SUCCESS; + break; + } + } + + return Status; +} + +NTSTATUS CQueue::GetSecureElementId(SECURE_ELEMENT_TYPE eType, GUID *pSecureElementId) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + *pSecureElementId = GUID_NULL; + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + (pEntry != &m_SecureElementList); + pEntry = pEntry->Flink) { + if (CSecureElement::FromListEntry(pEntry)->GetType() == eType) { + *pSecureElementId = CSecureElement::FromListEntry(pEntry)->GetIdentifier(); + Status = STATUS_SUCCESS; + break; + } + } + + return Status; +} + |
