Windows PC/SC and SCARD_E_SERVICE_STOPPED (part 2)

In a previous blog article " Windows PC/SC and SCARD_E_SERVICE_STOPPED " I presented a problem on Windows when the last reader is disconnected.

After a discussion on the pcsclite-muscle mailing list I got nice feedback.Thanks.

Diego de Felice gave a reference to a Microsoft documentation from 2016 "What's New in Smart Cards"

Smart Card Service start and stop behavior

Smart card reader detection logic has been added so that the Smart Card Service runs only when appropriate. On Windows Server 2012 and Windows 8, the Smart Card Service (scardsvr) automatically starts when the user connects a smart card reader and automatically stops when a user removes a smart card reader and no other smart card reader is connected to the computer. On startup, the Smart Card Service automatically starts if a reader was previously connected to the computer but a reader is not currently connected to the system. If no smart card readers are connected to the computer, the service will automatically shut down one minute after the last API call into the Smart Card Service. If a reader was never previously connected to the computer, the service will not start automatically.

So the (strange) behavior is documented by Microsoft.

Stephan Brunner then gave a solution: establish a new context.


Source code

I wanted to test the hack proposed by Stephan.

#! /usr/bin/env python3

from smartcard.scard import *
from smartcard.pcsc.PCSCExceptions import *
import time

hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
    raise EstablishContextException(hresult)

i = 0
while True:
    hresult, readers = SCardListReaders(hcontext, [])
    exc = BaseSCardException(message="", hresult=hresult)
    print('(%d) %s:' % (i, str(exc)), readers)
    if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
        hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
        if hresult != SCARD_S_SUCCESS:
            raise EstablishContextException(hresult)

    time.sleep(1)
    i += 1

Output

(base) C:\Users\ludovic\Documents>python SCardListReaders.py
(0) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(1) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(2) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(3) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(4) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(5) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(6) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(7) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(8) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(9) : Le gestionnaire de ressources des cartes ŕ puce s’est arręté.  (0x8010001E): []
(10) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(11) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(12) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(13) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(14) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(15) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(16) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(17) : Le gestionnaire de ressources des cartes ŕ puce s’est arręté.  (0x8010001E): []
(18) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []
(19) : Impossible de trouver un lecteur de carte ŕ puce.  (0x8010002E): []

Description

Step 0, I start the script with no smart card reader connected. So SCardListReaders() returns the expected 0x8010002E which is SCARD_E_NO_READERS_AVAILABLE.

Step 4, Then I connect a smart card reader.

Step 9, Then I disconnect the smart card reader and SCardListReaders() returns the unexpected error 0x8010001E i.e. SCARD_E_SERVICE_STOPPED.

The program then calls SCardEstablishContext() again, with success, and continue.

Step 10, The next call to CardListReaders() returns the expected 0x8010002E. So I guess the PC/SC resource manager has been  re-started. 

Step 13, Connecting a new smart card reader works fine and the reader is detected.

Step 17, I disconnect the reader and I get again the SCARD_E_SERVICE_STOPPED error.

Without the hack

If the code does not establish a new context then CardListReaders() will return SCARD_E_SERVICE_STOPPED for ever. And if I connect a new reader it is not detected.

I think this behavior explains the bug reported in CardRequest waitforcard ATR can't see reconnected readers on Windows (regression) #123.


Conclusion

Ignoring SCARD_E_SERVICE_STOPPED, as I did in commit 5b43ef5, is not a correct solution. I have to work on a better solution (just for Windows).

Windows PC/SC and SCARD_E_SERVICE_STOPPED

It looks like Windows introduced a new behavior when the last smart card reader is disconnected. The PC/SC layer (WinSCard library) will return the error SCARD_E_SERVICE_STOPPED.

According to MSDN "Smart Card Return Values":

SCARD_E_SERVICE_STOPPED
0x8010001E
The smart card resource manager has shut down. 

 

Demonstration

The idea is to wait for a smart card reader if none is already present.

Then wait for the card reader to be disconnected.

Once disconnected SCardListReaders() will return SCARD_E_SERVICE_STOPPED (0x8010001E) instead of the expected SCARD_E_NO_READERS_AVAILABLE (0x8010002E).


Source code

#! /usr/bin/env python3

from smartcard.scard import *
from smartcard.pcsc.PCSCExceptions import *
import time

hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
    raise EstablishContextException(hresult)

hresult, readers = SCardListReaders(hcontext, [])
if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
    raise ListReadersException(hresult)
print('PC/SC Readers:', readers)

if not len(readers):
    print("Wait for the first reader")
    while not len(readers):
        hresult, readers = SCardListReaders(hcontext, [])
        if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
            raise ListReadersException(hresult)
        time.sleep(1)
    print('PC/SC Readers:', readers)

print("wait for the last reader removal")
while len(readers):
    hresult, readers = SCardListReaders(hcontext, [])
    if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
        raise ListReadersException(hresult)
    time.sleep(1)
print('PC/SC Readers:', readers)

hresult = SCardReleaseContext(hcontext)
if hresult != SCARD_S_SUCCESS:
    raise ReleaseContextException(hresult)

GNU/Linux result

rousseau@debian:~ $ ./SCardListReaders.py
PC/SC Readers: []
Wait for the first reader
PC/SC Readers: ['Alcor Micro AU9540 00 00']
wait for the last reader removal
PC/SC Readers: []

Windows result

(base) C:\Users\ludovic\Documents>python SCardListReaders.py
PC/SC Readers: []
Wait for the first reader
PC/SC Readers: ['Generic EMV Smartcard Reader 0']
wait for the last reader removal
Traceback (most recent call last):
  File "C:\Users\ludovic\Documents\SCardListReaders.py", line 29, in <module>
    raise ListReadersException(hresult)
smartcard.pcsc.PCSCExceptions.ListReadersException: Failed to list readers:
 Le gestionnaire de ressources des cartes ŕ puce s’est arręté. (0x8010001E)

Sorry for the error message in French. I don't know an equivalent of "LANG=C program" to have program run and use the default language (English).

You can also note the strange display of accented letters. Windows is not yet Unicode ready? :-)


Problems

The problem is that this new behavior broke code in PySCard, my PC/SC Python wrapper.

For example see

Yes, it is an old issue. I tried to fix it many times but the fixes has some other side effects and the problem is still present somewhere.

Windows is NOT my preferred platform (far from it) so fixing Windows issue is a P.I.T.A. for me. Help is really welcome here.

 

But Why?

What problem Microsoft is trying to solve with this behavior?

Why killing the resource manager and returning an error while there is still PC/SC clients connected to the resource manager?

If you have an idea of the answers please tell me.
If you know a way to change the behavior please tell me.

Use the pcsclite-muscle mailing list to discuss this issue.

New version of pcsc-lite: 1.9.5

I just released a new version of pcsc-lite 1.9.5.
pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.


Changes:

1.9.5: Ludovic Rousseau
4 December 2021

  • pcscd: autoexit even if no client connects
  • Fix variable substitution in systemd units
  • fix potential race conditions with powerState handling
  • Add and use tag TAG_IFD_DEVICE_REMOVED
  • UnitaryTests: port code to Python 3

 

This version includes some user visible changes (if the user looks very carefully).

pcscd: autoexit even if no client connects

Since pcsc-lite version 1.8.0 (November 2010) pcscd can be started by systemd when a PC/SC application is started. See pcscd auto start using systemd.

After the last client releases the last PC/SC resource the pcscd daemon is waiting for 30 seconds (in case a new client connects again) and then exits. This mechanism works great and will continue to work.

When pcscd is NOT started by a PC/SC application then the autoexit mechanism is not triggered (the alarm clock is not started) and the pcscd process can run forever if no client connects and disconnects.

This happens when pcscd is manually started (or restarted) using something like:

sudo service pcscd restart

This is used to relaunch pcscd after a pcscd upgrade or after a driver installation or upgrade. I implemented this to fix Debian bug #995814.


Add and use tag TAG_IFD_DEVICE_REMOVED

pcscd knows when a USB reader is removed. The hotplug mechanism, by default, uses libudev.

pcscd will now notify the driver that the USB device has been removed and that it is not a good idea to try to send USB packets to a now inexistent device. This will prevent the driver to log USB communication errors (because the device is no more present).

I implemented this to (partly) fix "Reader disconnects are noisy in journal/logs #110"

The driver needs to handle this tag. It is the case for my CCID driver since the patch "Avoid logging errors when a reader is removed". The next stable CCID driver version will include this change.

macOS Monterey and smart cards status

Monterey (macOS 12.0 or 12.0.1 in fact) is now available since October, 2021.

I compare this version to the previous one in Big Sur I presented in macOS Big Sur and smart cards status.



PC/SC

Since Yosemite (macOS 10.10 in 2014) the PC/SC layer is no more a fork of pcsc-lite. So comparing versions with pcsc-lite is useless.

$ cat /System/Library/Frameworks/PCSC.framework/Versions/A/Resources/version.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>BuildAliasOf</key>
	<string>CryptoTokenKit</string>
	<key>BuildVersion</key>
	<string>13</string>
	<key>CFBundleShortVersionString</key>
	<string>8.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>ProjectName</key>
	<string>SmartCardServices</string>
	<key>SourceVersion</key>
	<string>554040005000000</string>
</dict>
</plist>

The BuildVersion changed from 25 to 13. No idea what that means.

The SourceVersion changed from 487040010000000 to 554040005000000. Again, no idea what that means. Except that Apple made changes to the code.

I have not yet made many tests of the PC/SC layer. So far it works fine.


Crypto Token Kit

CryptoTokenKit is the native smart card API since the complete rewrite in macOS Yosemite 10.10 (OS X Yosemite BETA and smart cards status).

The framework directory changed again. There is no more a /System/Library/Frameworks/CryptoTokenKit.framework/CryptoTokenKit/ directory.

But you can of course still use the CryptoTokenKit framework. My Objective-C sample "PC/SC" sample in Objective-C (synchronous) still builds and works fine.


CCID

$ grep -A 1 CFBundleShortVersionString /usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/Info.plist
17:    <key>CFBundleShortVersionString</key>
18-    <string>1.4.34</string>
The CCID driver has been upgraded from version 1.4.32 as in Big Sur to version 1.4.34.
 
I released version 1.4.34 in  January 2021. The latest CCID version (at the time of writing this blog) is 1.4.36 from August 2021.

Apple Open Source

Apple has not yet added macOS Monterey to the Apple Open Source web site. So we can't say much about what Apple provides in Monterey.

macOS Monterey 12.0.1 Release Notes

CryptoTokenKit is not mentioned in the macOS Monterey 12.0.1 Release Notes. So I imagine only minor changes were made.

Conclusion

No big changes in Monterey for the smart card world.

PCSC sample in Flutter/Dart

Here is a new PC/SC sample in Dart language I promised in PC/SC sample in different languages.

Fabien Rousseau wrote a PC/SC wrapper for Dart (the language) and Flutter (the UI toolkit).

The library is at https://pub.dev/packages/flutter_pcsc. It is available for GNU/Linux, macOS and Windows.

The source code is at https://github.com/fabienrousseau/flutter_pcsc and uses the MIT license. The current version is 0.0.4.

Fabien also provides an sample code at https://github.com/fabienrousseau/flutter_pcsc/blob/main/flutter_pcsc/example/lib/main.dart. I reused this code for my example bellow.

 

Installation

I just cloned the flutter_pcsc git repository.

You also need to install the flutter SDK to build and run the application. But if you use Flutter I imagine your already have the SDK installed.


Source code

You can use flutter create test to create a sample application with the correct structure and configuration files.

Then edit the pubspec.yaml file to add in the dependencies: section something like:

dependencies:
  flutter_pcsc:
    path: /home/rousseau/Documents/github/flutter_pcsc/flutter_pcsc

Of course you adapt the path to the location of your flutter_pcsc directory.

The source code of the sample application lib/main.dart is:

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';

import 'package:flutter_pcsc/flutter_pcsc.dart';

void main() {
  MyApp? myApp;

  runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    FlutterError.onError = (FlutterErrorDetails details) {
      FlutterError.dumpErrorToConsole(details);
      myApp?.addError(details.toString());
    };

    runApp(myApp = MyApp());
  }, (Object error, StackTrace stack) {
    myApp?.addError(error.toString());
  });
}

class MyApp extends StatelessWidget {
  final GlobalKey<_MyAppBodyState> _myAppKey = GlobalKey();

  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('PCSC example app'),
          ),
          body: MyAppBody(key: _myAppKey)),
    );
  }

  void addError(String msg) {
    _myAppKey.currentState?.messages.add(Message.error(msg));
  }
}

class MyAppBody extends StatefulWidget {
  const MyAppBody({required Key key}) : super(key: key);

  @override
  _MyAppBodyState createState() {
    return _MyAppBodyState();
  }
}

enum MessageType { info, error }

class Message {
  final String content;
  final MessageType type;
  Message(this.type, this.content);

  static info(String content) {
    return Message(MessageType.info, content);
  }

  static error(String content) {
    return Message(MessageType.error, content);
  }
}

class _MyAppBodyState extends State<MyAppBody> {
  static const List<int> selectAppletCommand = [
    0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01
  ];
  static const List<int> appCommand = [
    0x00, 0x00, 0x00, 0x00
  ];
  final ScrollController _scrollController = ScrollController();

  final List<Message> messages = [];

  @override
  void initState() {
    super.initState();
    helloWorld();
  }

  Future<void> helloWorld() async {
    /* establish PCSC context */
    int ctx = await Pcsc.establishContext(PcscSCope.user);
    CardStruct? card;
    try {
      /* get the reader list */
      List<String> readers = await Pcsc.listReaders(ctx);

      if (readers.isEmpty) {
        messages.add(Message.error('Could not detect any reader'));
      } else {
        /* use the first reader */
        String reader = readers[0];
        setState(() {
          messages.add(Message.info('Using reader: $reader'));
        });

        /* connect to the card */
        card = await Pcsc.cardConnect(
            ctx, reader, PcscShare.shared, PcscProtocol.any);

        /* send select applet APDU */
        var response = await Pcsc.transmit(card, selectAppletCommand);
            messages.add(Message.info('Card returned: ${hexDump(response)}'));

        /* send applet test command */
        response = await Pcsc.transmit(card, appCommand);
        var sw = response.sublist(response.length - 2);
        var bytes = response.sublist(0, response.length - 2);
        var text = utf8.decode(bytes);

        messages.add(Message.info('Card returned: ${hexDump(response)}'));

        if (sw[0] != 0x90 || sw[1] != 0x00) {
          setState(() {
            messages
                .add(Message.error('Card returned an error: ${hexDump(sw)}'));
          });
        } else {
          setState(() {
            messages.add(Message.info('text is: ${text}'));
          });
        }
      }
    } finally {
      if (card != null) {
        try {
          /* disconnect from the card */
          await Pcsc.cardDisconnect(card.hCard, PcscDisposition.resetCard);
        } on Exception catch (e) {
          messages.add(Message.error(e.toString()));
        }
      }
      try {
        /* release PCSC context */
        await Pcsc.releaseContext(ctx);
      } on Exception catch (e) {
        messages.add(Message.error(e.toString()));
      }
    }
  }

  static String hexDump(List<int> csn) {
    return csn
        .map((i) => i.toRadixString(16).padLeft(2, '0').toUpperCase())
        .join(' ');
  }

  _scrollToBottom() {
    _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
  }

  @override
  Widget build(BuildContext context) {
    TextStyle errorStyle = const TextStyle(color: Colors.red);
    WidgetsBinding.instance?.addPostFrameCallback((_) => _scrollToBottom());
    return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
      Expanded(
          child: Column(children: [
        Expanded(
            child: ListView(
                controller: _scrollController,
                children: messages
                    .map((e) => Text(e.content,
                        style: e.type == MessageType.error ? errorStyle : null))
                    .toList())),
        Container(
            margin: const EdgeInsets.all(10),
            child: ElevatedButton(
                onPressed: () async {
                  await tryAgain();
                },
                child: const Text("Try again")))
      ]))
    ]);
  }

  tryAgain() async {
    messages.clear();
    await helloWorld();
  }
}

Output

$ flutter run
hanging current working directory to: /home/rousseau/Documents/flutter/blog
Launching lib/main.dart on Linux in debug mode...
Building Linux application...                                           
Syncing files to device Linux...                                    55ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

💪 Running with sound null safety 💪

An Observatory debugger and profiler on Linux is available at:
http://127.0.0.1:34019/ORqpNzBQLEQ=/
The Flutter DevTools debugger and profiler on Linux is available at:
http://127.0.0.1:9100?uri=http://127.0.0.1:34019/ORqpNzBQLEQ=/

Flutter is a framework to create graphical applications. So when you run the sample code you get a new window.

Remarks

The code is long because it is a complete Flutter application so you have to handle the graphical interface.

I asked Fabien for a Dart only interface but flutter_pcsc depends on some Flutter mechanisms to handle asynchronism. So it is not possible to write a PC/SC application for the console just using Dart. But if you use Dart I guess you also use flutter.

Despite of his (nice) name Fabien Rousseau is not part of my direct family. So I do not have a potential conflict of interest here 😜.


Conclusion

Thanks to Fabien for telling about his Dart/Flutter project.

If you work on a Free Software PC/SC wrapper that is not yet in my list please let me know.

What happened 20 years ago?

The 14th of October 2001 I became a Debian Developer (or DD).

You can get some more details from https://nm.debian.org/person/rousseau/

History of my Debian packages

Unsurprisingly I mostly maintain smart card related packages.

2001

  • pcsc-perl Perl interface to the PC/SC smart card library
  • pcsc-tools Some tools to use with smart cards and PC/SC

2002

  • ifd-gempc
    • libgempc410 - PC/SC driver for the GemPC 410, 412, 413 and 415 smart card readers
    • libgempc430 - PC/SC driver for the GemPC 430, 432, 435 smart card readers
  • pcsc-lite
    • pcscd - Middleware to access a smart card using PC/SC (daemon side) 
    • libpcsclite1 - Middleware to access a smart card using PC/SC (library) 
    • libpcsclite-dev - Middleware to access a smart card using PC/SC (development files)

2003

  • ccid PC/SC driver for USB CCID smart card readers
  • pilot-link
    • libpisock9 - library for communicating with a PalmOS PDA
    • libpisync1 - synchronization library for PalmOS devices
    • pilot-link - tools to communicate with a PalmOS PDA
    • python-pisock - Python module to communicate with PalmOS PDA
    • libpisock-dev - development files for communicating with a PalmOS PDA
    • python-pisock-dbg - Python module to communicate with PalmOS PDA (debug extension)

    • I will stoped maintaining pilot-link in 2019 because I do not have a Palm pilot any more.

2004

  • jpilot 
    • jpilot - graphical app. to modify the contents of your Palm Pilot's DBs
    • jpilot-plugins - plugins for jpilot (Palm Pilot desktop)
       
    • I will stoped maintaining jpilot in 2019 for the same reason I stopped maintaining pilot-link

2005

  • asedriveiiie
    • libasedrive-serial - PC/SC driver for the Athena ASEDrive IIIe serial smart card reader
    • libasedrive-usb - PC/SC driver for the Athena ASEDrive IIIe USB smart card reader  

2007

  • coolkey
    • coolkey - Smart Card PKCS #11 cryptographic module
    • libckyapplet1 - Smart Card Coolkey applet
    • libckyapplet1-dev - Smart Card Coolkey applet development files 

2008

  • pykcs11 - PKCS#11 wrapper for Python
  • pyscard - Python3 wrapper above PC/SC API

2009

  • pam-pkcs11 - Fully featured PAM module for using PKCS#11 smart cards
  • colormake - simple wrapper around make to colorize output

2013

  • vgrabbj - grabs an image from a camera and puts it in jpg/png format
  • acsccid - PC/SC driver for ACS USB CCID smart card readers
    • I just sponsor this package because the package maintainer is not a Debian Developer

2016

  • grisbi - personal finance management program

2017

  • 0ad - Real-time strategy game of ancient warfare
  • 0ad-data - Real-time strategy game of ancient warfare (data files)

2020

  • libnfc
    • libnfc-bin - Near Field Communication (NFC) binaries
    • libnfc-dev - Near Field Communication (NFC) library (development files)
    • libnfc-examples - Near Field Communication (NFC) examples
    • libnfc-pn53x-examples - Near Field Communication (NFC) examples for PN53x chips only
    • libnfc6 - Near Field Communication (NFC) library 

You can have a list of my packages and their status at my Debian QA page.


Why?

I maintain Debian packages for different reasons.

  • I maintain the package upstream.
    • For example I am the author and maintainer of the CCID driver. And I also maintain the Debian package for this software.
  • I use the package and it has been orphaned. It may have been orphaned in Debian or also orphaned upstream.
    • For example I am not the original author of pcsc-lite. I first started providing bug reports, then patches. I got a write access on the source code repository. I made new releases. And eventually I am on the only maintainer of pcsc-lite.


Conclusion

I think Debian is a very nice project. I do plan to continue maintaining my packages for the next 10 or 20 years. Maybe more :-)

No more error logs when a USB reader is removed

When a USB reader was removed you got error logs from the CCID driver. Something like:

00000000 [140295925790464] ccid_usb.c:871:WriteUSB() write failed (1/22): -4 LIBUSB_ERROR_NO_DEVICE
00000506 [140295934183168] ccid_usb.c:871:WriteUSB() write failed (1/22): -4 LIBUSB_ERROR_NO_DEVICE

 

Why?

The first error is because IFDHICCPresence() sends the CCID command PC_to_RDR_GetSlotStatus to know if a card is present.

The second error is because IFDHCloseChannel() sends the CCID command PC_to_RDR_IccPowerOff to power off the card.

But since the USB device has been removed these 2 CCID commands fails and logs the error.  

 

Solution

I had to modify both pcsc-lite (the middleware) and libccid (the smart card reader driver) so they both cooperate to fix the problem.

Now when pcscd detects that a reader has been removed it uses IFDHSetCapabilities() with the new tag TAG_IFD_DEVICE_REMOVED to indicate to the driver that the reader has been removed and that there is no point in trying to send commands to a no more connected reader.

 

Bug reports

The problem has been initially reported by a Fedora user at "Bug 2011128 - pcscd spam syslog when the laptop is undocked and the reader is connected through the dock".

Then Jakub Jelen from Red Hat forwarded the bug upstream (to me) at "Reader disconnects are noisy in journal/logs #110" so I am aware of the problem.

The problem was then fixed 5 days later in "Use TAG_IFD_DEVICE_REMOVED when the reader is removed" for pcsc-lite and "Avoid logging errors when a reader is removed" for libccid.

Report upstream

The important point here is that users must reports bugs to their distribution. And distributions must report the problems upstream. It works well (in general).

What does not work is if you know of a problem and the problem does not come to me. For example this bug "Memory leak in pcscd" has been reported to pfSense 3 months ago and no one from pfSense took the time to report the bug upstream (to me). They just discussed about how to remove/disable pcsc-lite from pfSense instead.

I don't have a pfSense account so I can't comment on their bug tracker. I can't create an account on the bug trackers of every project that use my software. That is not how it is supposed to work. Distributors should forward bugs to the upstream projects.

I don't know if the problem is because pfSense is a commercial product or if it is because it uses FreeBSD. (be careful, a troll may be hidden in the previous sentence).


Conclusion

Don't be shy, report bugs.

Don't be shy, forward bugs upstream.

New version of pcsc-lite: 1.9.4

I just released a new version of pcsc-lite 1.9.4.
pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.

This version fixes a memory leak when libusb is used for the hotplug mechanism. GNU/Linux systems should use libudev instead of libusb for hotplug so they should not be concerned. FreeBSD systems and other non-Linux systems had the problem.

The memory leak problem was also reported on the pfSense project at https://redmine.pfsense.org/issues/12095.

Changes:

1.9.4: Ludovic Rousseau
1 October 2021

  •  fix a memory leak when libusb is used for hotplug (i.e. non-Linux systems)

PySCard 2.0.2 released

I just released a new version 2.0.2 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

This version is a bug fix release.

Changes:

  • Fix an issue in ReaderMonitor() that failed to enumerate readers on macOS
  • getReaderNames(): handle SCARD_E_SERVICE_STOPPED on Windows generated on last reader removal
  • Restrict MAX_BUFFER_SIZE_EXTENDED to 65535 and fix an issue on Lenovo laptops with NXP NFC readers
  • SmartcardException: display a positive PCSC error code

New version of libccid: 1.4.36

I just released version 1.4.36 of libccid the Free Software CCID class smart card reader driver.

Changes:

1.4.36 - 30 August 2021, Ludovic Rousseau

  • Add support of
    • Lenovo Lenovo Smartcard Wired Keyboard II
    • REINER SCT tanJack USB
    • SafeNet eToken 5110+ FIPS
    • SafeNet eToken 5300 C
    • jSolutions s.r.o. Multi SIM card reader 4/8
  • parse: fix check when bNumDataRatesSupported = 0