Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Monday, December 27, 2021

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).

Sunday, December 26, 2021

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.

Saturday, December 4, 2021

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.