pcscd runs as pcscd user

With pcsc-lite 2.4.0 (New version of pcsc-lite: 2.4.0) the pcscd daemon process now runs (if systemd is used) as user pcscd in group pcscd.

Possible problem

For example if I use libccid version 1.6.2 (so before the update to set the access rights, implemented in version 1.7.0, New version of libccid: 1.7.0) I get the error:

$ journalctl -u pcscd -f
oct. 21 21:41:26 MacBookPro pcscd[25848]: 00000000 ccid_usb.c:564:OpenUSBByName() Can't libusb_open(4/6): LIBUSB_ERROR_ACCESS

If I start pcscd in foreground and debug mode I get more details:

$ sudo -u pcscd -g pcscd LIBCCID_ifdLogLevel=0x000F pcscd --foreground --debug
00000000 [140406854371648] ../src/debuglog.c:395:DebugLogSetLevel() debug level=debug
00000369 [140406854371648] ../src/configfile.l:293:DBGetReaderListDir() Parsing conf directory: /etc/reader.conf.d
00000055 [140406854371648] ../src/configfile.l:326:DBGetReaderListDir() Skipping non regular file: ..
00000010 [140406854371648] ../src/configfile.l:365:DBGetReaderList() Parsing conf file: /etc/reader.conf.d/libccidtwin
00000096 [140406854371648] ../src/configfile.l:326:DBGetReaderListDir() Skipping non regular file: .
00000037 [140406854371648] ../src/pcscdaemon.c:669:main() pcsc-lite 2.4.0 daemon ready.
00000111 [140406854371648] ../src/pcscdaemon.c:752:main() Using drivers directory: /usr/lib/pcsc/drivers
00008375 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/003/001
00000275 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/003/001
00000240 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8242, path: /dev/bus/usb/003/002
00000311 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/003/001
00000325 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x0237, path: /dev/bus/usb/003/003
00000237 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x0237, path: /dev/bus/usb/003/003
00000237 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x0237, path: /dev/bus/usb/003/003
00000408 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/002/001
00000239 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/002/001
00000271 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8507, path: /dev/bus/usb/002/002
00000417 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/004/001
00000246 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/004/001
00000254 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x0A5C, PID: 0x4500, path: /dev/bus/usb/004/002
00000266 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8213, path: /dev/bus/usb/004/003
00000254 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8213, path: /dev/bus/usb/004/003
00000266 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8213, path: /dev/bus/usb/004/003
00000258 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8213, path: /dev/bus/usb/004/003
00000298 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x0A5C, PID: 0x4500, path: /dev/bus/usb/004/002
00000242 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/004/001
00000260 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x08E6, PID: 0x3438, path: /dev/bus/usb/004/006
00000186 [140406854371648] ../src/hotplug_libudev.c:430:HPAddDevice() Adding USB device: Gemalto USB Shell Token V2
00000089 [140406854371648] ../src/readerfactory.c:1103:RFInitializeReader() Attempting startup of Gemalto USB Shell Token V2 00 00 using /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux/libccid.so
00000597 [140406854371648] ../src/readerfactory.c:977:RFBindFunctions() Loading IFD Handler 3.0
00000083 [140406854371648] ../src/ifdhandler.c:2130:init_driver() Driver version: 1.6.2
00001019 [140406854371648] ../src/ifdhandler.c:2152:init_driver() LogLevel: 0x0003
00000012 [140406854371648] ../src/ifdhandler.c:2163:init_driver() DriverOptions: 0x0000
00000370 [140406854371648] ../src/ifdhandler.c:2176:init_driver() LogLevel from LIBCCID_ifdLogLevel: 0x000F
00000040 [140406854371648] ../src/ifdhandler.c:93:CreateChannelByNameOrChannel() Lun: 0, device: usb:08e6/3438:libudev:0:/dev/bus/usb/004/006
00000011 [140406854371648] ../src/ccid_usb.c:261:OpenUSBByName() Reader index: 0, Device: usb:08e6/3438:libudev:0:/dev/bus/usb/004/006
00000069 [140406854371648] ../src/ccid_usb.c:293:OpenUSBByName() interface_number: 0
00000008 [140406854371648] ../src/ccid_usb.c:294:OpenUSBByName() usb bus/device: 4/6
00000006 [140406854371648] ../src/ccid_usb.c:331:OpenUSBByName() Using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist
00001026 [140406854371648] ../src/ccid_usb.c:349:OpenUSBByName() ifdManufacturerString: Ludovic Rousseau (ludovic.rousseau@free.fr)
00000008 [140406854371648] ../src/ccid_usb.c:350:OpenUSBByName() ifdProductString: Generic CCID driver
00000007 [140406854371648] ../src/ccid_usb.c:351:OpenUSBByName() Copyright: This driver is protected by terms of the GNU Lesser General Public License version 2.1, or (at your option) any later version.
00011059 [140406854371648] ../src/ccid_usb.c:435:OpenUSBByName() Try device: 4/6
00000062 [140406854371648] ../src/ccid_usb.c:445:OpenUSBByName() vid/pid : 08E6/3438
00000007 [140406854371648] ../src/ccid_usb.c:561:OpenUSBByName() Checking device: 4/6
00000005 [140406854371648] ../src/ccid_usb.c:632:OpenUSBByName() Trying to open USB bus/device: 4/6
00000136 [140406854371648] ../src/ccid_usb.c:638:OpenUSBByName() Can't libusb_open(4/6): LIBUSB_ERROR_ACCESS
00000706 [140406854371648] ../src/ccid_usb.c:208:close_libusb_if_needed() libusb_exit
00000239 [140406854371648] ../src/ccid_usb.c:892:OpenUSBByName() Device not found?
00000103 [140406854371648] ../src/ifdhandler.c:134:CreateChannelByNameOrChannel() failed
00000042 [140406854371648] ../src/readerfactory.c:1144:RFInitializeReader() Open Port 0x200000 Failed (usb:08e6/3438:libudev:0:/dev/bus/usb/004/006)
00000007 [140406854371648] ../src/readerfactory.c:371:RFAddReader() Gemalto USB Shell Token V2 init failed.
00000008 [140406854371648] ../src/readerfactory.c:627:RFRemoveReader() UnrefReader() count was: 1
00000005 [140406854371648] ../src/readerfactory.c:1157:RFUnInitializeReader() Attempting shutdown of Gemalto USB Shell Token V2 00 00.
00000006 [140406854371648] ../src/readerfactory.c:1014:RFUnloadReader() Unloading reader driver.
00000830 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/001/001
00000225 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/001/001
00000223 [140406854371648] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x05AC, PID: 0x8403, path: /dev/bus/usb/001/003

And the reader is not available for PC/SC applications:

$ pcsc_scan -r
No reader found.

Solution

The reader driver must set the access rights of the device file, such as /dev/bus/usb/004/006 in the example above, so that the pcscd group has read and write access rights.

$ ls -l /dev/bus/usb/004/006
crw-rw-r--+ 1 root pcscd 189, 389 21 oct.  21:41 /dev/bus/usb/004/006

Please note that the file belongs to the pcscd group and not the root group.

Implementation

Create a udev rule file that contains something like:

# If not adding the device, go away
ACTION!="add", GOTO="foobar_rules_end"
SUBSYSTEM!="usb", GOTO="foobar_rules_end"
ENV{DEVTYPE}!="usb_device", GOTO="foobar_rules_end"

# CherrySmartTerminalST2XXX.txt
ATTRS{idVendor}=="046A", ATTRS{idProduct}=="003E", GROUP="pcscd"

# All done
LABEL="foobar_rules_end"

For example the libccid udev rule file is available at https://salsa.debian.org/rousseau/CCID/-/blob/master/src/92_pcscd_ccid.rules?ref_type=heads#L12. The important part for the generic CCID driver is:

# Generic CCID device (bInterfaceClass = 0x0b)
ENV{ID_USB_INTERFACES}=="*:0b0000:*", GROUP="pcscd"

For earch USB device with an interface equal to 0x0b, i.e. CCID class, then set the group to pcscd.

The pcscd group should be created by the installation of pcsc-lite package. If it is not the case for your distribution, you should report it as a bug to your distribution pcsc-lite package.

Build requirements

To build the CCID driver with this new feature, you need to have udev pkg-config file installed.

You need to be able to get the udevdir variable from the udev.pc file:

$ pkg-config --variable=udevdir udev
/usr/lib/udev

For Debian/Ubuntu and derivatives distributions just install the package systemd-dev.

Conclusion

This change significantly improves your system security. A bug in pcsc-lite, or in a smart card reader driver used by pcsc-lite, will not allow an attacker to gain access to the root account. The attacker would only have access to a normal/unprivileged account.

This mechanism cannot be used everywhere, for example on systems without systemd or udev. However, most GNU/Linux systems should be covered.