GnuPG and PC/SC conflicts, episode 3

After GnuPG and PC/SC conflicts and GnuPG and PC/SC conflicts, episode 2 we now have a 3rd episode.

Executive summary

Your file ~/.gnupg/scdaemon.conf should contain:

disable-ccid
pcsc-shared

PC/SC access sharing

More and more applications are using smart cards. So they all need to behave cooperatively to share the access to the card.

PC/SC provides a way to connect to a smart card and get an exclusive access to it. That is the parameter dwShareMode of SCardConnect(). It can take 3 values:

  • SCARD_SHARE_SHARED - This application will allow others to share the reader.

  • SCARD_SHARE_EXCLUSIVE - This application will NOT allow others to share the reader.

  • SCARD_SHARE_DIRECT - Direct control of the reader, even without a card. SCARD_SHARE_DIRECT can be used before using SCardControl() to send control commands to the reader even if a card is not present in the reader. Contrary to Windows winscard behavior, the reader is accessed in shared mode and not exclusive mode.

If you use SCARD_SHARE_EXCLUSIVE then no other application can use the reader, and the card inserted in the reader, until the application calls SCardDisconnect().

If you try to use SCARD_SHARE_EXCLUSIVE while another application already has a connection to the reader then your own call fails with SCARD_E_SHARING_VIOLATION. Only one application can connect to the reader when SCARD_SHARE_EXCLUSIVE is used.

Problem with GnuPG

A user reported an issue on PC/SC because he was not able to use GnuPG2 in Yubikey 5 with gpg2 only initialized properly after restarting pcscd #215.

He gets the output:

$ gpg2 --card-status
gpg: selecting card failed: No such device
gpg: OpenPGP card not available: No such device

I tried to reproduce the problem and log the GnuPG PC/SC calls as described in PCSC API spy using LIBPCSCLITE_DELEGATE. Unfortunately the environment variable LIBPCSCLITE_DELEGATE is ignored by scdaemon (GnuPG program used to access the smart card). I guess that is because scdaemon sanitizes its environment to limit attacks.

I had to slightly modify pcsc-lite to force the use of the spying library:

diff --git a/src/libredirect.c b/src/libredirect.c
index ac023f4..e40d7f6 100644
--- a/src/libredirect.c
+++ b/src/libredirect.c
@@ -127,7 +127,7 @@ static void log_line(const char *fmt, ...)

 static LONG load_lib(void)
 {
-#define LIBPCSC "libpcsclite_real.so.1"
+#define LIBPCSC "libpcscspy.so.0"

    const char *lib;

From the PC/SC calls the problem is obvious:

SCardEstablishContext
 i dwScope: SCARD_SCOPE_SYSTEM (0x00000002)
 o hContext: 0x43293756
 => SCARD_S_SUCCESS [0x00000000]  [0.010792]
SCardListReaders
 i hContext: 0x43293756
 i mszGroups: (null)
 o pcchReaders: 0x0000001A
 o mszReaders: NULL
 => SCARD_S_SUCCESS [0x00000000]  [0.000161]
SCardListReaders
 i hContext: 0x43293756
 i mszGroups: (null)
 o pcchReaders: 0x0000001A
 o mszReaders: Alcor Micro AU9540 00 00
 o mszReaders: 
 => SCARD_S_SUCCESS [0x00000000]  [0.000287]
SCardConnect
 i hContext: 0x43293756
 i szReader Alcor Micro AU9540 00 00
 i dwShareMode: SCARD_SHARE_EXCLUSIVE (0x00000001)
 i dwPreferredProtocols: 0x00000003 (T=0, T=1)
 i phCard 0x00000000 (0)
 i pdwActiveProtocol 0x00000000 (0)
 o phCard 0x00000000 (0)
 o dwActiveProtocol: T=1 (0x00000002)
 => SCARD_E_SHARING_VIOLATION [0x8010000B]  [0.007124]
SCardReleaseContext
 i hContext: 0x43293756
 => SCARD_S_SUCCESS [0x00000000]  [0.000101]

The call to SCardConnect() fails with SCARD_E_SHARING_VIOLATION.

Werner Koch (GnuPG author & maintainer) added a comment in the issue:

I then modified my ~/.gnupg/scdaemon.conf file to contain:

disable-ccid
pcsc-shared

And it now works better:

SCardEstablishContext
 i dwScope: SCARD_SCOPE_SYSTEM (0x00000002)
 o hContext: 0x4EFB8700
 => SCARD_S_SUCCESS [0x00000000]  [0.011911]
SCardListReaders
 i hContext: 0x4EFB8700
 i mszGroups: (null)
 o pcchReaders: 0x0000001A
 o mszReaders: NULL
 => SCARD_S_SUCCESS [0x00000000]  [0.000177]
SCardListReaders
 i hContext: 0x4EFB8700
 i mszGroups: (null)
 o pcchReaders: 0x0000001A
 o mszReaders: Alcor Micro AU9540 00 00
 o mszReaders: 
 => SCARD_S_SUCCESS [0x00000000]  [0.000162]
SCardConnect
 i hContext: 0x4EFB8700
 i szReader Alcor Micro AU9540 00 00
 i dwShareMode: SCARD_SHARE_SHARED (0x00000002)
 i dwPreferredProtocols: 0x00000003 (T=0, T=1)
 i phCard 0x00000000 (0)
 i pdwActiveProtocol 0x00000000 (0)
 o phCard 0x1EF6C421 (519488545)
 o dwActiveProtocol: T=1 (0x00000002)
 => SCARD_S_SUCCESS [0x00000000]  [0.009658]
SCarControl
 i hCard: 0x1EF6C421
 i dwControlCode: CM_IOCTL_GET_FEATURE_REQUEST (0x42000D48)
 i bSendLength 0x00000000 (0)
 i bSendBuffer
 i  NULL
 o bRecvLength 0x00000006 (6)
 o bRecvBuffer
 o  0000 12 04 42 33 00 12                               ..B3..
 => SCARD_S_SUCCESS [0x00000000]  [0.000267]
  parsing CM_IOCTL_GET_FEATURE_REQUEST results:
  Tag FEATURE_GET_TLV_PROPERTIES is 0x42330012
[...]

The call to SCardConnect() now works ang GnuPG continues with SCarControl(), etc.

GnuPG history

The GnuPG code uses PCSC_SHARE_EXCLUSIVE since the first version supporting PC/SC in commit 1bcf8ef9dea1a9b171c27ef48cadb79df6201e33.

Author: Werner Koch <wk@gnupg.org>
Date:   Tue Aug 5 17:11:04 2003 +0000

    Cleanups, fixes and PC/SC support

+  err = pcsc_connect (reader_table[slot].pcsc.context,
+                      portstr? portstr : list,
+                      PCSC_SHARE_EXCLUSIVE,
+                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+                      &reader_table[slot].pcsc.card,
+                      &reader_table[slot].pcsc.protocol);

The option to use PCSC_SHARE_SHARED was introduced in 2021 with commit 5732e7a8e97cebf8e850c472e644e2a9b040836f.

Author: Werner Koch <wk@gnupg.org>
Date:   Fri Mar 12 09:21:57 2021 +0100

    scd: New option --pcsc-shared.

    * scd/scdaemon.h (opt): Add field opcsc_shared.
    * scd/scdaemon.c (opcscShared): New.
    (opts): Add "--pcsc-shared".
    (main): Set flag.
    * scd/apdu.c (connect_pcsc_card): Use it.
    (pcsc_get_status): Take flag in account.
    * scd/app-openpgp.c (cache_pin): Bypass in shared mode.
    (verify_chv2: Do not auto verify chv1 in shared mode.
    * scd/app-piv.c (cache_pin): By pass caceh in shared mode.
    --

    This option should in general not be used.  The patch tries to limit
    bad effects but using shared mode is somewhat dangerous depending on
    the other PC/SC users.

   err = pcsc_connect (pcsc.context,
                       reader_table[slot].rdrname,
-                      PCSC_SHARE_EXCLUSIVE,
+                      opt.pcsc_shared? PCSC_SHARE_SHARED:PCSC_SHARE_EXCLUSIVE,
                       PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
                       &reader_table[slot].pcsc.card,
                       &reader_table[slot].pcsc.protocol);

Temporary exclusive access

It is possible to get an exclusive access to a card but still share the reader with other applications.

The idea is to request an exclusive access for a short time, only when needed, using PC/SC transactions.

SCardBeginTransaction()

Establishes a temporary exclusive access mode for doing a series of commands in a transaction.

You might want to use this when you are selecting a few files and then writing a large file so you can make sure that another application will not change the current file. If another application has a lock on this reader or this application is in SCARD_SHARE_EXCLUSIVE the function will block until it can continue.

SCardEndTransaction()

Ends a previously begun transaction.

The calling application must be the owner of the previously begun transaction or an error will occur.

The application connects to the reader using SCARD_SHARE_SHARED. And then uses SCardBeginTransaction()/SCardEndTransaction() to send a list of APDU without any interruption. Typically the application does something like:

  1. start a transaction

  2. submit the user PIN code

  3. perform a protected operation like signing

  4. invalidate the user PIN code

  5. end the transaction

With this algorithm another application cannot inject a command between steps 1 and 5 and benefit from the verified user PIN.

GnuPG and transactions

I had a look at the GnuPG source code and no PC/SC transaction is ever used.

Conclusion

Using an exclusive access at SCardConnect() is problematic in a multi applications system.

Use PC/SC transactions instead.