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:
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:
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:
start a transaction
submit the user PIN code
perform a protected operation like signing
invalidate the user PIN code
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.