Click here to Skip to main content
Click here to Skip to main content

An UMDF Driver for a Virtual Smart Card Reader

By , 23 Dec 2010
 
BixVReader.zip
exports.def
VirtualSmartCard
Properties
Settings.settings
win7
BixVReader.inx
makefile
sources
winXP
BixVReader.inx
makefile
sources
BixVReader_binaries.zip
Win7_x64
BixVReader.dll
BixVReader.inf
Win7_x86
BixVReader.dll
BixVReader.inf
WinXP_x86
BixVReader.dll
BixVReader.inf
#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);
}

By viewing downloads associated with this article you agree to the Terms of use and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Fabio Ottavi
Engineer
Italy Italy
Member
No Biography provided

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 23 Dec 2010
Article Copyright 2010 by Fabio Ottavi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid