#include "internal.h"
#include "VirtualSCReader.h"
#include "queue.h"
#include "device.h"
#include "driver.h"
#include <stdio.h>
#include <winscard.h>
#include "memory.h"
#include <Sddl.h>
HRESULT
CMyDevice::CreateInstance(
__in IWDFDriver *FxDriver,
__in IWDFDeviceInitialize * FxDeviceInit
)
/*++
Routine Description:
This method creates and initializs an instance of the virtual smart card reader driver's
device callback object.
Arguments:
FxDeviceInit - the settings for the device.
Device - a location to store the referenced pointer to the device object.
Return Value:
Status
--*/
{
inFunc
CComObject<CMyDevice>* device = NULL;
HRESULT hr;
//
// Allocate a new instance of the device class.
//
hr = CComObject<CMyDevice>::CreateInstance(&device);
if (device==NULL)
{
return E_OUTOFMEMORY;
}
//
// Initialize the instance.
//
device->AddRef();
FxDeviceInit->SetLockingConstraint(WdfDeviceLevel);
CComPtr<IUnknown> spCallback;
hr = device->QueryInterface(IID_IUnknown, (void**)&spCallback);
CComPtr<IWDFDevice> spIWDFDevice;
if (SUCCEEDED(hr))
{
hr = FxDriver->CreateDevice(FxDeviceInit, spCallback, &spIWDFDevice);
}
if (spIWDFDevice->CreateDeviceInterface(&SmartCardReaderGuid,NULL)!=0)
OutputDebugString(L"CreateDeviceInterface Failed");
SAFE_RELEASE(device);
return hr;
}
void CMyDevice::IoSmartCardIsPresent(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
UNREFERENCED_PARAMETER(outBufSize);
OutputDebugString(L"[IPRE]IOCTL_SMARTCARD_IS_PRESENT");
BYTE ATR[100];
DWORD ATRSize;
if (QueryATR(ATR,&ATRSize))
// there's a smart card present, so complete the request
pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
else {
// there's no smart card present, so leave the request pending; it will be completed later
waitInsertIpr=pRequest;
IRequestCallbackCancel *callback;
QueryInterface(__uuidof(IRequestCallbackCancel),(void**)&callback);
pRequest->MarkCancelable(callback);
callback->Release();
}
}
void CMyDevice::IoSmartCardGetState(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
UNREFERENCED_PARAMETER(outBufSize);
OutputDebugString(L"[GSTA]IOCTL_SMARTCARD_GET_STATE");
BYTE ATR[100];
DWORD ATRSize;
if (!QueryATR(ATR,&ATRSize)) {
OutputDebugString(L"[GSTA]SCARD_ABSENT");
setInt(pRequest,SCARD_ABSENT);
}
else {
OutputDebugString(L"[GSTA]SCARD_SPECIFIC");
setInt(pRequest,SCARD_SPECIFIC);
}
}
void CMyDevice::IoSmartCardIsAbsent(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
UNREFERENCED_PARAMETER(outBufSize);
OutputDebugString(L"[IABS]IOCTL_SMARTCARD_IS_ABSENT");
BYTE ATR[100];
DWORD ATRSize;
if (!QueryATR(ATR,&ATRSize))
// there's no smart card present, so complete the request
pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
else {
// there's a smart card present, so leave the request pending; it will be completed later
waitRemoveIpr=pRequest;
IRequestCallbackCancel *callback;
QueryInterface(__uuidof(IRequestCallbackCancel),(void**)&callback);
pRequest->MarkCancelable(callback);
callback->Release();
}
}
void CMyDevice::IoSmartCardPower(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
UNREFERENCED_PARAMETER(outBufSize);
OutputDebugString(L"[POWR]IOCTL_SMARTCARD_POWER");
DWORD code=getInt(pRequest);
if (code==SCARD_COLD_RESET) {
OutputDebugString(L"[POWR]SCARD_COLD_RESET");
}
else if (code==SCARD_WARM_RESET) {
OutputDebugString(L"[POWR]SCARD_WARM_RESET");
}
else if (code==SCARD_POWER_DOWN) {
OutputDebugString(L"[POWR]SCARD_POWER_DOWN");
}
if (code==SCARD_COLD_RESET || code==SCARD_WARM_RESET) {
BYTE ATR[100];
DWORD ATRsize;
if (!QueryATR(ATR,&ATRsize,true))
{
pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);
return;
}
setBuffer(pRequest,ATR,ATRsize);
}
else {
pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
}
}
void CMyDevice::IoSmartCardSetAttribute(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
UNREFERENCED_PARAMETER(outBufSize);
OutputDebugString(L"[SATT]IOCTL_SMARTCARD_SET_ATTRIBUTE");
IWDFMemory *inmem=NULL;
pRequest->GetInputMemory(&inmem);
SIZE_T size;
BYTE *data=(BYTE *)inmem->GetDataBuffer(&size);
DWORD minCode=*(DWORD*)(data);
bool handled=false;
if (minCode==SCARD_ATTR_DEVICE_IN_USE) {
OutputDebugString(L"[SATT]SCARD_ATTR_DEVICE_IN_USE");
pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
handled=true;
}
inmem->Release();
if (!handled) {
wchar_t log[300];
swprintf(log,L"[SATT]ERROR_NOT_SUPPORTED:%08X",minCode);
OutputDebugString(log);
pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
}
}
void CMyDevice::IoSmartCardTransmit(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
UNREFERENCED_PARAMETER(outBufSize);
OutputDebugString(L"[TRSM]IOCTL_SMARTCARD_TRANSMIT");
BYTE APDU[1000];
int APDUSize;
getBuffer(pRequest,APDU,&APDUSize);
if (((SCARD_IO_REQUEST*)APDU)->dwProtocol!=SCARD_PROTOCOL_T1) {
pRequest->CompleteWithInformation(STATUS_INVALID_DEVICE_STATE, 0);
return;
}
BYTE Resp[1000];
int RespSize;
if (!QueryTransmit(APDU+sizeof(SCARD_IO_REQUEST),APDUSize-sizeof(SCARD_IO_REQUEST),Resp+sizeof(SCARD_IO_REQUEST),&RespSize))
{
pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);
return;
}
((SCARD_IO_REQUEST*)Resp)->cbPciLength=sizeof(SCARD_IO_REQUEST);
((SCARD_IO_REQUEST*)Resp)->dwProtocol=SCARD_PROTOCOL_T1;
setBuffer(pRequest,Resp,RespSize+sizeof(SCARD_IO_REQUEST));
}
void CMyDevice::IoSmartCardGetAttribute(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
UNREFERENCED_PARAMETER(inBufSize);
wchar_t log[300];
DWORD code=getInt(pRequest);
swprintf(log,L"[GATT] - code %0X",code);
OutputDebugString(log);
switch(code) {
case SCARD_ATTR_CHARACTERISTICS:
// 0x00000000 No special characteristics
OutputDebugString(L"[GATT]SCARD_ATTR_CHARACTERISTICS");
setInt(pRequest,0);
return;
case SCARD_ATTR_VENDOR_NAME:
OutputDebugString(L"[GATT]SCARD_ATTR_VENDOR_NAME");
setString(pRequest,"Bix",(int)outBufSize);
return;
case SCARD_ATTR_VENDOR_IFD_TYPE:
OutputDebugString(L"[GATT]SCARD_ATTR_VENDOR_IFD_TYPE");
setString(pRequest,"VIRTUAL_CARD_READER",(int)outBufSize);
return;
case SCARD_ATTR_DEVICE_UNIT:
OutputDebugString(L"[GATT]SCARD_ATTR_DEVICE_UNIT");
setInt(pRequest,0);
return;
case SCARD_ATTR_ATR_STRING:
OutputDebugString(L"[GATT]SCARD_ATTR_ATR_STRING");
BYTE ATR[100];
DWORD ATRsize;
if (!QueryATR(ATR,&ATRsize))
{
pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);
return;
}
setBuffer(pRequest,ATR,ATRsize);
return;
case SCARD_ATTR_CURRENT_PROTOCOL_TYPE:
OutputDebugString(L"[GATT]SCARD_ATTR_CURRENT_PROTOCOL_TYPE");
setInt(pRequest,SCARD_PROTOCOL_T1); // T=1
return;
default:
swprintf(log,L"[GATT]ERROR_NOT_SUPPORTED:%08X",code);
OutputDebugString(log);
pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
}
}
void CMyDevice::ProcessIoControl(__in IWDFIoQueue* pQueue,
__in IWDFIoRequest* pRequest,
__in ULONG ControlCode,
SIZE_T inBufSize,
SIZE_T outBufSize)
{
inFunc
UNREFERENCED_PARAMETER(pQueue);
wchar_t log[300];
swprintf(log,L"[IOCT]IOCTL %08X - In %i Out %i",ControlCode,inBufSize,outBufSize);
OutputDebugString(log);
if (ControlCode==IOCTL_SMARTCARD_GET_ATTRIBUTE) {
IoSmartCardGetAttribute(pRequest,inBufSize,outBufSize);
return;
}
else if (ControlCode==IOCTL_SMARTCARD_IS_PRESENT) {
IoSmartCardIsPresent(pRequest,inBufSize,outBufSize);
return;
}
else if (ControlCode==IOCTL_SMARTCARD_GET_STATE) {
IoSmartCardGetState(pRequest,inBufSize,outBufSize);
return;
}
else if (ControlCode==IOCTL_SMARTCARD_IS_ABSENT) {
IoSmartCardIsAbsent(pRequest,inBufSize,outBufSize);
return;
}
else if (ControlCode==IOCTL_SMARTCARD_POWER) {
IoSmartCardPower(pRequest,inBufSize,outBufSize);
return;
}
else if (ControlCode==IOCTL_SMARTCARD_SET_ATTRIBUTE) {
IoSmartCardSetAttribute(pRequest,inBufSize,outBufSize);
return;
}
else if (ControlCode==IOCTL_SMARTCARD_TRANSMIT) {
IoSmartCardTransmit(pRequest,inBufSize,outBufSize);
return;
}
swprintf(log,L"[IOCT]ERROR_NOT_SUPPORTED:%08X",ControlCode);
OutputDebugString(log);
pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
return;
}
BOOL CreateMyDACL(SECURITY_ATTRIBUTES * pSA)
{
TCHAR * szSD = TEXT("D:") // Discretionary ACL
TEXT("(D;OICI;GA;;;BG)") // Deny access to
// built-in guests
TEXT("(D;OICI;GA;;;AN)") // Deny access to
// anonymous logon
TEXT("(A;OICI;GRGWGX;;;AU)") // Allow
// read/write/execute
// to authenticated
// users
TEXT("(A;OICI;GA;;;BA)"); // Allow full control
// to administrators
if (NULL == pSA)
return FALSE;
return ConvertStringSecurityDescriptorToSecurityDescriptor(
szSD,
SDDL_REVISION_1,
&(pSA->lpSecurityDescriptor),
NULL);
}
bool CMyDevice::CheckATR() {
if (pipe==NULL)
return false;
DWORD read=0;
DWORD command=1;
if (!WriteFile(pipe,&command,sizeof(DWORD),&read,NULL)) {
return false;
}
FlushFileBuffers(pipe);
DWORD size=0;
if (!ReadFile(pipe,&size,sizeof(DWORD),&read,NULL)) {
return false;
}
if (size==0)
return false;
BYTE ATR[100];
if (!ReadFile(pipe,ATR,size,&read,NULL)) {
return false;
}
return true;
}
bool CMyDevice::QueryTransmit(BYTE *APDU,int APDUlen,BYTE *Resp,int *Resplen) {
if (pipe==NULL)
return false;
DWORD command=2;
DWORD read=0;
if (!WriteFile(pipe,&command,sizeof(DWORD),&read,NULL)) {
pipe=NULL;
return false;
}
DWORD dwAPDUlen=(DWORD)APDUlen;
if (!WriteFile(pipe,&dwAPDUlen,sizeof(DWORD),&read,NULL)) {
pipe=NULL;
return false;
}
if (!WriteFile(pipe,APDU,APDUlen,&read,NULL)) {
pipe=NULL;
return false;
}
FlushFileBuffers(pipe);
DWORD dwRespLen;
if (!ReadFile(pipe,&dwRespLen,sizeof(DWORD),&read,NULL)) {
pipe=NULL;
return false;
}
if (!ReadFile(pipe,Resp,dwRespLen,&read,NULL)) {
pipe=NULL;
return false;
}
(*Resplen)=(int)dwRespLen;
return true;
}
bool CMyDevice::QueryATR(BYTE *ATR,DWORD *ATRsize,bool reset) {
if (pipe==NULL)
return false;
DWORD command=reset ? 0 : 1;
DWORD read=0;
if (!WriteFile(pipe,&command,sizeof(DWORD),&read,NULL)) {
pipe=NULL;
return false;
}
FlushFileBuffers(pipe);
DWORD size=0;
if (!ReadFile(pipe,&size,sizeof(DWORD),&read,NULL)) {
pipe=NULL;
return false;
}
if (size==0)
return false;
if (!ReadFile(pipe,ATR,size,&read,NULL)) {
pipe=NULL;
return false;
}
(*ATRsize)=size;
return true;
}
DWORD WINAPI pipeServerFunc(CMyDevice *device) {
return device->pipeServer();
}
DWORD CMyDevice::pipeServer() {
SECURITY_ATTRIBUTES sa;
CreateMyDACL(&sa);
while (true) {
HANDLE _pipe=CreateNamedPipe(L"\\\\.\\pipe\\SCardSimulatorDriver",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE,PIPE_UNLIMITED_INSTANCES,0,0,0,&sa);
HANDLE _eventpipe=CreateNamedPipe(L"\\\\.\\pipe\\SCardSimulatorDriverEvents",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE,PIPE_UNLIMITED_INSTANCES,0,0,0,&sa);
OutputDebugString(L"Pipe created");
ConnectNamedPipe(_pipe,NULL);
ConnectNamedPipe(_eventpipe,NULL);
OutputDebugString(L"Pipe connected");
pipe=_pipe;
eventpipe=_eventpipe;
if (waitInsertIpr!=NULL) {
// if I'm waiting for card insertion, verify if there's a card present
if (CheckATR()) {
if (waitInsertIpr->UnmarkCancelable()==S_OK)
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
waitInsertIpr=NULL;
}
}
while (true) {
// wait for a command
DWORD command=0;
DWORD read=0;
if (!ReadFile(eventpipe,&command,sizeof(DWORD),&read,NULL)) {
pipe=NULL;
eventpipe=NULL;
if (waitRemoveIpr!=NULL) {// card inserted
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
waitRemoveIpr=NULL;
}
if (waitInsertIpr!=NULL) {// card removed
waitInsertIpr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
waitInsertIpr=NULL;
}
break;
}
if (command==0 && waitRemoveIpr!=NULL) {// card removed
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
waitRemoveIpr=NULL;
}
else if (command==1 && waitInsertIpr!=NULL) {// card inserted
if (waitInsertIpr->UnmarkCancelable()==S_OK)
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
waitInsertIpr=NULL;
}
}
}
OutputDebugString(L"Pipe quit!!!");
return 0;
}
/////////////////////////////////////////////////////////////////////////
//
// CMyDevice::OnPrepareHardware
//
// Called by UMDF to prepare the hardware for use.
//
// Parameters:
// pWdfDevice - pointer to an IWDFDevice object representing the
// device
//
// Return Values:
// S_OK: success
//
/////////////////////////////////////////////////////////////////////////
HRESULT CMyDevice::OnPrepareHardware(
__in IWDFDevice* pWdfDevice
)
{
inFunc
// Store the IWDFDevice pointer
m_pWdfDevice = pWdfDevice;
// Configure the default IO Queue
HRESULT hr = CMyQueue::CreateInstance(m_pWdfDevice, this);
DWORD pipeThreadID;
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)pipeServerFunc,this,0,&pipeThreadID);
return hr;
}
/////////////////////////////////////////////////////////////////////////
//
// CMyDevice::OnReleaseHardware
//
// Called by WUDF to uninitialize the hardware.
//
// Parameters:
// pWdfDevice - pointer to an IWDFDevice object for the device
//
// Return Values:
// S_OK:
//
/////////////////////////////////////////////////////////////////////////
HRESULT CMyDevice::OnReleaseHardware(
__in IWDFDevice* pWdfDevice
)
{
inFunc
UNREFERENCED_PARAMETER(pWdfDevice);
// Release the IWDFDevice handle, if it matches
if (pWdfDevice == m_pWdfDevice.p)
{
m_pWdfDevice.Release();
}
return S_OK;
}
HRESULT CMyDevice::OnD0Entry(IN IWDFDevice* pWdfDevice,IN WDF_POWER_DEVICE_STATE previousState) {
UNREFERENCED_PARAMETER(pWdfDevice);
UNREFERENCED_PARAMETER(previousState);
OutputDebugString(L"OnD0Entry");
return S_OK;
}
HRESULT CMyDevice::OnD0Exit(IN IWDFDevice* pWdfDevice,IN WDF_POWER_DEVICE_STATE newState) {
UNREFERENCED_PARAMETER(pWdfDevice);
UNREFERENCED_PARAMETER(newState);
OutputDebugString(L"OnD0Exit");
return S_OK;
}
void CMyDevice::shutDown(){
if (waitRemoveIpr!=NULL) {
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
waitRemoveIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
waitRemoveIpr=NULL;
}
if (waitInsertIpr!=NULL) {
if (waitInsertIpr->UnmarkCancelable()==S_OK)
waitInsertIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
waitInsertIpr=NULL;
}
}
HRESULT CMyDevice::OnQueryRemove(IN IWDFDevice* pWdfDevice) {
UNREFERENCED_PARAMETER(pWdfDevice);
OutputDebugString(L"OnQueryRemove");
shutDown();
return S_OK;
}
HRESULT CMyDevice::OnQueryStop(IN IWDFDevice* pWdfDevice) {
UNREFERENCED_PARAMETER(pWdfDevice);
OutputDebugString(L"OnQueryStop");
shutDown();
return S_OK;
}
void CMyDevice::OnSurpriseRemoval(IN IWDFDevice* pWdfDevice) {
UNREFERENCED_PARAMETER(pWdfDevice);
OutputDebugString(L"OnSurpriseRemoval");
shutDown();
}
STDMETHODIMP_ (void) CMyDevice::OnCancel(IN IWDFIoRequest* pWdfRequest) {
OutputDebugString(L"OnCancel Request");
if (pWdfRequest==waitRemoveIpr) {
OutputDebugString(L"Cancel Remove");
waitRemoveIpr=NULL;
}
else if (pWdfRequest==waitInsertIpr) {
OutputDebugString(L"Cancel Insert");
waitInsertIpr=NULL;
}
pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
}