Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Sept/Oct 2019 Issue of The NT Insider available


Download PDF here: http://insider.osr.com/2019/ntinsider_2019_01.pdf

It’s a particularly BIG issue, too: 40 pages of technical goodness, ranging from WDF to Minifilters. Check it out.
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

Virtual Smart Card Reader error in monitoring card status.

minhptaminhpta Member Posts: 9

Dear experts,

I am developing a virtual smart card reader but I am sticking on make it visible to Smart Card Resource Manager.
My source code is based on this guide **https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard/ ** and Windows-driver-samples\smartcrd.
However, I just make it very simple as I can understand.

SmartcardDeviceControl return STATUS_DEVICE_BUSY(0x80000011) for IoControlCode IOCTL_SMARTCARD_IS_ABSENT(0x31002C or 3211308 in decimal) in callback routine EvtIoDeviceControl.

I guess this is a problem prevent my reader visible to Smart Card Resource Manager but I don't know how to fix it.
I am new in this field. Any guidance would be really helpful.

Please check my source code in the first comment.

And this is how I install the driver.

devcon install VPJSCReader.inf root\VPJSCReader
Device node created. Install is complete when drivers are installed...
Updating drivers for root\VPJSCReader from c:\Users\minh\Downloads\VPJSCReader\VPJSCReader.inf.
Drivers installed successfully.

Here is debugger's log.

VPJSCReader!DriverEntry: Enter - KMDF Version Built Oct 14 2019 08:50:44
VPJSCReader!DriverEntry: Exit 0
VPJSCReader!VPJSCReaderEvtDeviceAdd: Enter
VPJSCReader!VPJSCReaderEvtDeviceAdd: Exit
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 36 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 36 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211304
VPJSCReader!CBCardTracking: Entry
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 0 IoControlCode 3211320
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211308
VPJSCReaderLibComplete called: status 80000011, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211308
VPJSCReaderLibComplete called: status 80000011, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211308
VPJSCReaderLibComplete called: status 80000011, Context 00001B784F5051D8

In the last three IO request, it return status 80000011.

Here is my device manager.

And here is my event viewer.

Comments

  • minhptaminhpta Member Posts: 9

    My driver source code.

    NTSTATUS
    DriverEntry(
        _In_ PDRIVER_OBJECT  DriverObject,
        _In_ PUNICODE_STRING RegistryPath
        )
    {
        WDF_DRIVER_CONFIG config;
        NTSTATUS status;
            SmartcardSetDebugLevel(DEBUG_ALL);
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!DriverEntry: Enter - KMDF Version Built %s %s\n",
                __DATE__, __TIME__)
        );
            WDF_DRIVER_CONFIG_INIT(&config,
                               VPJSCReaderEvtDeviceAdd
                               );
            status = WdfDriverCreate(DriverObject,
                                 RegistryPath,
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &config,
                                 WDF_NO_HANDLE
                                 );
            if (!NT_SUCCESS(status)) {
            KdPrint(("WdfDriverCreate failed with status 0x%x\n", status));
            return status;
        }
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!DriverEntry: Exit %x\n",
                status)
        );
            return status;
    }
    NTSTATUS
    VPJSCReaderEvtDeviceAdd(
        _In_    WDFDRIVER       Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit
        )
    {
        NTSTATUS status;
            UNREFERENCED_PARAMETER(Driver);
            PAGED_CODE();
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!VPJSCReaderEvtDeviceAdd: Enter\n")
        );
            status = VPJSCReaderCreateDevice(DeviceInit);
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!VPJSCReaderEvtDeviceAdd: Exit\n")
        );
            return status;
    }
    NTSTATUS
    VPJSCReaderCreateDevice(
        _Inout_ PWDFDEVICE_INIT DeviceInit
        )
    {
        WDF_OBJECT_ATTRIBUTES deviceAttributes;
        PDEVICE_CONTEXT deviceContext = NULL;
        WDFDEVICE device;
        NTSTATUS status;
            PAGED_CODE();
            WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SMARTCARD);
        WdfDeviceInitSetExclusive(DeviceInit, TRUE);
        WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
            WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
        deviceAttributes.EvtCleanupCallback = VPJSCReaderEvtDriverContextCleanup;
            status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
            if (NT_SUCCESS(status)) {       
            deviceContext = DeviceGetContext(device);
                deviceContext->Device = device;
            InterlockedIncrement((PLONG)& deviceContext->DeviceInstanceNo);
                status = WdfDeviceCreateDeviceInterface(
                device,
                &SmartCardReaderGuid,
                NULL // ReferenceString
                );
                if (NT_SUCCESS(status)) {
                status = VPJSCReaderQueueInitialize(device);
            }
        }
            if (!NT_SUCCESS(status)) {
            return status;
        }
                return VPJSCReaderRegisterWithSmcLib(deviceContext);
    }
    NTSTATUS
    VPJSCReaderRegisterWithSmcLib(
        PDEVICE_CONTEXT DeviceContext
    ) {
        PREADER_EXTENSION ReaderExtension;
        PSMARTCARD_EXTENSION SmartcardExtension;
        NTSTATUS status;
            PAGED_CODE();
            //   set up the device extension.
        SmartcardExtension = &DeviceContext->SmartcardExtension;
            //   allocate the reader extension
        ReaderExtension = ExAllocatePoolWithTag(
            NonPagedPoolNx,
            sizeof(READER_EXTENSION),
            SMARTCARD_POOL_TAG
        );
            if (ReaderExtension == NULL) {
            SmartcardLogError(
                WdfDriverWdmGetDriverObject(WdfGetDriver()),
                STATUS_INSUFFICIENT_RESOURCES,
                NULL,
                0
            );
            status = STATUS_INSUFFICIENT_RESOURCES;
            return status;
        }
            RtlZeroMemory(ReaderExtension, sizeof(READER_EXTENSION));
        SmartcardExtension->ReaderExtension = ReaderExtension;
            //   setup smartcard extension - callback's
        SmartcardExtension->ReaderFunction[RDF_CARD_POWER] = CBCardPower;
        SmartcardExtension->ReaderFunction[RDF_TRANSMIT] = CBTransmit;
        SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] = CBCardTracking;
        SmartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] = CBSetProtocol;
            RtlCopyMemory( SmartcardExtension->VendorAttr.VendorName.Buffer, VPJSC_VENDOR_NAME, sizeof(VPJSC_VENDOR_NAME));
        SmartcardExtension->VendorAttr.VendorName.Length = sizeof(VPJSC_VENDOR_NAME);
            RtlCopyMemory(SmartcardExtension->VendorAttr.IfdType.Buffer, VPJSC_IFD_TYPE, sizeof(VPJSC_IFD_TYPE));
        SmartcardExtension->VendorAttr.IfdType.Length = sizeof(VPJSC_IFD_TYPE);
            SmartcardExtension->VendorAttr.UnitNo = DeviceContext->DeviceInstanceNo;
            SmartcardExtension->VendorAttr.IfdVersion.BuildNumber = 0;
            //   store firmware revision in ifd version
        SmartcardExtension->VendorAttr.IfdVersion.VersionMajor = 0;
        SmartcardExtension->VendorAttr.IfdVersion.VersionMinor = 0;
        SmartcardExtension->VendorAttr.IfdSerialNo.Length = 0;
            //   setup smartcard extension - reader capabilities
        SmartcardExtension->ReaderCapabilities.SupportedProtocols = SCARD_PROTOCOL_T0;
            SmartcardExtension->ReaderCapabilities.ReaderType = SCARD_READER_TYPE_VENDOR;
            SmartcardExtension->ReaderCapabilities.MechProperties = 0;
        SmartcardExtension->ReaderCapabilities.Channel = 0;
            SmartcardExtension->ReaderCapabilities.CLKFrequency.Default = 4000;
        SmartcardExtension->ReaderCapabilities.CLKFrequency.Max = 4000;
            // reader could support higher data rates
        SmartcardExtension->ReaderCapabilities.DataRatesSupported.List = 0;
        SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries = 0;
        SmartcardExtension->ReaderCapabilities.DataRate.Default = 9600;
        SmartcardExtension->ReaderCapabilities.DataRate.Max = 9600;
            SmartcardExtension->Version = SMCLIB_VERSION;
        SmartcardExtension->SmartcardRequest.BufferSize = MIN_BUFFER_SIZE;
        SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
            SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT;
            status = SmartcardInitialize(SmartcardExtension);
        if (status != STATUS_SUCCESS) {
                SmartcardLogError(
                WdfDriverWdmGetDriverObject(WdfGetDriver()),
                STATUS_INSUFFICIENT_RESOURCES,
                NULL,
                0
            );
            return status;
        }
            SmartcardExtension->OsData->DeviceObject = WdfDeviceWdmGetDeviceObject(DeviceContext->Device);
            return status;
    }
    
    NTSTATUS
    CBCardPower(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE,("VPJSCReader!CBCardPower: Entry\n"));   
        return STATUS_NO_MEDIA;
    }
    NTSTATUS
    CBSetProtocol(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBSetProtocol: Entry\n"));
        return STATUS_NO_MEDIA;
    }
    NTSTATUS
    CBTransmit(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBTransmit: Entry\n"));
        return STATUS_NO_MEDIA;
    }
    NTSTATUS
    CBCardTracking(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBCardTracking: Entry\n"));
        return STATUS_SUCCESS;
    }
    
    
    _Function_class_(IO_COMPLETION_ROUTINE)
    NTSTATUS
    VPJSCReaderLibComplete(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp,
        IN PVOID Context
    )
    {
        UNREFERENCED_PARAMETER(DeviceObject);
    
        KdPrint(("VPJSCReaderLibComplete called: status %x, Context %p\n", Irp->IoStatus.Status, Context));
    
        WdfRequestComplete((WDFREQUEST)Context, Irp->IoStatus.Status);
    
        return STATUS_MORE_PROCESSING_REQUIRED;
    }
    
    VOID
    VPJSCReaderEvtIoDeviceControl(
        _In_ WDFQUEUE Queue,
        _In_ WDFREQUEST Request,
        _In_ size_t OutputBufferLength,
        _In_ size_t InputBufferLength,
        _In_ ULONG IoControlCode
        )
    {    
        PDEVICE_CONTEXT deviceExtension;
        PIRP irp;
    
        KdPrint(("VPJSCReaderEvtIoDeviceControl Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d\n",
            Queue, Request, (int) OutputBufferLength, (int) InputBufferLength, IoControlCode));
    
        deviceExtension = DeviceGetContext(WdfIoQueueGetDevice(Queue));
    
        irp = WdfRequestWdmGetIrp(Request);
    
        SET_REQUEST_IN_IRP(irp, Request);
    
        IoCopyCurrentIrpStackLocationToNext(irp);
    
        IoSetCompletionRoutine(irp,
            VPJSCReaderLibComplete,
            Request,
            TRUE,
            TRUE,
            TRUE
        );
    
        IoSetNextIrpStackLocation(irp);
    
        (VOID)SmartcardDeviceControl(&(deviceExtension->SmartcardExtension), irp);
    
        return;
    }
    
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,133

    There are a few problems here. Have you done any reading at all about the functions you are calling?

    I don't understand why you would use IoSetCompletionRoutine instead of WdfRequestSetCompletionRoutine. When an I/O completion routine is finished with an IRP, as yours is, you need to return the status from the IRP, and not STATUS_MORE_PROCESSING_REQUIRED. WdfRequestSetCompletionRoutine would have handled that for you.

    However, at a higher level, the whole completion routine processing is totally wrong. SmartcardDeviceControl is NOT passing the request into another driver. Go look at the documentation page. You do not need to worry about a stack location and a completion routine. That whole routine can be condensed to
    deviceExtension = DeviceGetContext...
    irp = WdfRequestWdmGetIrp(Request);
    return SmartcardDeviceControl(&(deviceExtension->SmartcardExtension), irp);

    Are you getting calls to your callbacks? SmartcardDeviceControl handles requests that don't need hardware, but anything that does need hardware has to call into your callbacks.

    Why did you SetExclusive on the device object? Did you read that somewhere?

    Your InterlockedIncrement of DeviceInstanceNo is silly. That routine will be called exactly once for each device, and each one will get a fresh device context. Thus, that number will never be anything other than 1.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • minhptaminhpta Member Posts: 9

    Hi @Tim_Roberts,
    Thank you very much for your comments.

    My source code is based on some resources such as https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard and Windows-driver-samples\smartcrd. I know this is not the whole picture and I've just focused on some functions related to smart card, not all of them.
    For me, there is a huge amount of knowledge in this filed and I've not understand it clearly.

    Are you getting calls to your callbacks? SmartcardDeviceControl handles requests that don't need hardware, but anything that does need hardware has to call into your callbacks.

    Yes, as you see in debugger's log, routine CBCardTracking is called. If I change

    SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT;
    

    to

    SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_POWERED;
    

    routine CBCardPower is also getting called.

    Now, I remove redundant code as your comments and the driver run same as it before changed.

    The problem may be in this callback routine CBCardTracking:

    NTSTATUS
        CBCardTracking(PSMARTCARD_EXTENSION SmartcardExtension) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBCardTracking: Entry\n"));
        return STATUS_SUCCESS;
    }
    

    Because, I do nothing here, just return success.
    I think that I need to do something with SmartcardExtension->OsData->NotificationIrp but I don't know how.

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,133

    OK, so you've established that the framework is fundamentally correct. Now you have to write the guts of your driver. That's where the real work is.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • minhptaminhpta Member Posts: 9

    Is there any one here who implement success a virtual smart card reader?
    Please help me, I am still sticking with the problem of my reader is not listed by SCardListReaders.

    My Event IO device control routine VPJSCReaderEvtIoDeviceControl is kept the same in above source code.
    And here what I receive after SmartcardDeviceControl is called(I am using WinDbg to get these information).

    For IoControlCode = IOCTL_SMARTCARD_IS_PRESENT, it returns Irp->IoStatus.Status = STATUS_SUCCESS => same as my expectation.
    For IoControlCode = IOCTL_SMARTCARD_GET_STATE,  it returns Irp->IoStatus.Status = STATUS_SUCCESS, state via Output Buffer = SCARD_ABSENT => same as my expectation.
    For IoControlCode = IOCTL_SMARTCARD_IS_ABSENT, it returns Irp->IoStatus.Status= STATUS_DEVICE_BUSY, not same as my expectation. It should be STATUS_SUCCESS.
    

    I think this problem here is a cause that prevent my reader listed by SCardListReaders. Any idea is welcome!

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
Writing WDF Drivers 21 Oct 2019 OSR Seminar Space & ONLINE
Internals & Software Drivers 18 Nov 2019 Dulles, VA
Kernel Debugging 30 Mar 2020 OSR Seminar Space
Developing Minifilters 27 Apr 2020 OSR Seminar Space & ONLINE