frankmorgner / vsmartcard

umbrella project for emulation of smart card readers or smart cards
http://frankmorgner.github.io/vsmartcard/
714 stars 200 forks source link

Virtual Smard Card Relay #193

Closed bowb closed 3 years ago

bowb commented 3 years ago

Version: did a build from master.

System: Ubuntu 18.04

Is it possible to pcsc-relay an already virtual smart card?

I am on ubuntu 18 and I have docker container that I have setup using pcsd and vcpd with a virtual javacard. I am trying to relay it so the host can use the card.

On the docker container

opensc-tool --list-readers
Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             Virtual PCD 00 00
1    No              Virtual PCD 00 01
pcsc-relay --connector pcsc --emulator vpcd  
Card's ATR: 3B DF 96 00 81 B1 FE 45 1F 83 80 73 CC 91 CB F9 A0 00 00 03
08 00 00 10 00 79 
Address already in use
Could not initialize connection to VPCD

running it on a different port gives the following but I don't know if this a valid configuration

pcsc-relay --connector pcsc --emulator vpcd -P 35966
Card's ATR: 3B DF 96 00 81 B1 FE 45 1F 83 80 73 CC 91 CB F9 A0 00 00 03
08 00 00 10 00 79 

On the host the config file and pcscd run:

FRIENDLYNAME "Virtual PCD"
DEVICENAME   localhost:0x8C7E
LIBPATH      /usr/local/lib/pcsc/drivers/serial/libifdvpcd.so
CHANNELID    0x8C7E

sudo pcscd -c  vpcd.local -afd
*** buffer overflow detected ***: pcscd terminated
Aborted

If this can be done how would the host be able to use the Virtual Smartcard running on the container?

bowb commented 3 years ago

I re-compiled virtualsmardcard with debugging on and the buffer overflow detected went away. I am getting different behavior with debugging flags turned on with virtualsmartcard.

./configure --sysconfdir=/etc  CFLAGS="-g -O0"
make
sudo make install

list readers on the host now returns a reader

# Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             Virtual PCD 00 00
1    No              Virtual PCD 00 01

The virtual card in the container is using jcard with a FIPS201 applet and when in the container it acts like I expect it to. pcsc_scan on the container

Scanning present readers...
0: Virtual PCD 00 00
1: Virtual PCD 00 01

Tue Mar 30 15:59:53 2021
 Reader 0: Virtual PCD 00 00
  Card state: Card inserted, 
  ATR: 3B DF 96 00 81 B1 FE 45 1F 83 80 73 CC 91 CB F9 A0 00 00 03 08 00 00 10 00 79

ATR: 3B DF 96 00 81 B1 FE 45 1F 83 80 73 CC 91 CB F9 A0 00 00 03 08 00 00 10 00 79
+ TS = 3B --> Direct Convention
+ T0 = DF, Y(1): 1101, K: 15 (historical bytes)
  TA(1) = 96 --> Fi=512, Di=32, 16 cycles/ETU
    250000 bits/s at 4 MHz, fMax for Fi = 5 MHz => 312500 bits/s
  TC(1) = 00 --> Extra guard time: 0
  TD(1) = 81 --> Y(i+1) = 1000, Protocol T = 1 
-----
  TD(2) = B1 --> Y(i+1) = 1011, Protocol T = 1 
-----
  TA(3) = FE --> IFSC: 254
  TB(3) = 45 --> Block Waiting Integer: 4 - Character Waiting Integer: 5
  TD(3) = 1F --> Y(i+1) = 0001, Protocol T = 15 - Global interface bytes following 
-----
  TA(4) = 83 --> Clock stop: state H - Class accepted by the card: (3G) A 5V B 3V 
+ Historical bytes: 80 73 CC 91 CB F9 A0 00 00 03 08 00 00 10 00
  Category indicator byte: 80 (compact TLV data object)
    Tag: 7, len: 3 (card capabilities)
      Selection methods: CC
        - DF selection by full DF name
        - DF selection by partial DF name
        - Implicit DF selection
        - Short EF identifier supported
      Data coding byte: 91
        - EF of TLV structure supported
        - Behaviour of write functions: one-time write
        - Value 'FF' for the first byte of BER-TLV tag fields: valid
        - Data unit in quartets: 2
      Command chaining, length fields and logical channels: CB
        - Command chaining
        - Extended Lc and Le fields
        - Logical channel number assignment: by the interface device
        - Maximum number of logical channels: 4
    Tag: F, len: 9 (application identifier)
      Application identifier: A0 00 00 03 08 00 00 10 00
+ TCK = 79 (correct checksum)

Possibly identified card (using /usr/share/pcsc/smartcard_list.txt):
3B DF 96 00 81 B1 FE 45 1F 83 80 73 CC 91 CB F9 A0 00 00 03 08 00 00 10 00 79
        Test PIV Cards available for sale from NIST
        http://csrc.nist.gov/groups/SNS/piv/testcards.html

On the host

Scanning present readers...
0: Virtual PCD 00 00
1: Virtual PCD 00 01

Tue Mar 30 10:01:26 2021
 Reader 0: Virtual PCD 00 00
  Card state: Card inserted, 
  ATR: 3B 80 80 01 01

ATR: 3B 80 80 01 01
+ TS = 3B --> Direct Convention
+ T0 = 80, Y(1): 1000, K: 0 (historical bytes)
  TD(1) = 80 --> Y(i+1) = 1000, Protocol T = 0 
-----
  TD(2) = 01 --> Y(i+1) = 0000, Protocol T = 1 
-----
+ Historical bytes: 
+ TCK = 01 (correct checksum)

Possibly identified card (using /home/test/.cache/smartcard_list.txt):
3B 80 80 01 01
    ISO 14443 Type B without historical bytes
    Electronic Passport
    Spanish passport (2012)
    Canadian Passport
    Venez_Prox

Other commands that work in the container do not work on the host. For example running the following on the container will prompt for the pin and dump the data but on the host it does not work.

pkcs11-tool -r -y data --login --application-id 2.16.840.1.101.3.7.2.96.48

Is this something that can be done?

frankmorgner commented 3 years ago

I'd be curious to know the source of your exception.

For your ATR, use pcsc-relay --vicc-atr=3BDF960081B1FE451F838073CC91CBF9A0000003080000100079 ...

bowb commented 3 years ago

Setting --vicc-atr=3BDF960081B1FE451F838073CC91CBF9A0000003080000100079 worked. I don't see this option in the documentation, but I do see it when I run pcsc-relay --help

the *** buffer overflow detected ***: pcscd terminated I think is coming from libc compile time flags for security and stack protection. https://wiki.ubuntu.com/ToolChain/CompilerFlags Any suggestions on how I could help you track this down?

Thanks for the help on the atr.

frankmorgner commented 3 years ago

you can run pcscd in valgrind, for example with debug symbols enabled. this should track and print memory violations:

sudo valgrind /usr/sbin/pcscd --foreground --debug --apdu
bowb commented 3 years ago

I compiled with ./configure --sysconfdir=/etc CFLAGS="-g -O1"

 sudo valgrind /usr/sbin/pcscd -c /home/bowb/card_test/vpcd.local -afd
==67469== Memcheck, a memory error detector
==67469== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==67469== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==67469== Command: /usr/sbin/pcscd -c /home/bowb/card_test/vpcd.local -afd
==67469== 
00000000 debuglog.c:289:DebugLogSetLevel() debug level=debug
00044683 configfile.l:361:DBGetReaderList() Parsing conf file: /home/bowb/card_test/vpcd.local
00015583 configfile.l:211:evaluatetoken() Add reader: Virtual PCD
00009584 readerfactory.c:1075:RFInitializeReader() Attempting startup of Virtual PCD 00 00 using /usr/local/lib/pcsc/drivers/serial/libifdvpcd.so
00014901 readerfactory.c:950:RFBindFunctions() Loading IFD Handler 3.0
00063981 ifd-vpcd.c:124:IFDHCreateChannel() Connected to virtual ICC on localhost port 35966
00000892 ifd-vpcd.c:290:IFDHGetCapabilities() unknown tag 4019
00000440 readerfactory.c:391:RFAddReader() Using the pcscd polling thread
00036789 readerfactory.c:524:RFAddReader() Driver is slot thread safe
00000444 readerfactory.c:1075:RFInitializeReader() Attempting startup of Virtual PCD 00 01 using /usr/local/lib/pcsc/drivers/serial/libifdvpcd.so
00002597 readerfactory.c:864:RFLoadReader() Reusing already loaded driver for /usr/local/lib/pcsc/drivers/serial/libifdvpcd.so
00000179 readerfactory.c:950:RFBindFunctions() Loading IFD Handler 3.0
00002168 ifd-vpcd.c:124:IFDHCreateChannel() Connected to virtual ICC on localhost port 35967
00000375 ifd-vpcd.c:290:IFDHGetCapabilities() unknown tag 4019
00000356 readerfactory.c:558:RFAddReader() Using the pcscd polling thread
00030594 pcscdaemon.c:662:main() pcsc-lite 1.8.23 daemon ready.
*** buffer overflow detected ***: /usr/sbin/pcscd terminated
==67469== 
==67469== Process terminating with default action of signal 6 (SIGABRT)
==67469==    at 0x5741FB7: raise (raise.c:51)
==67469==    by 0x5743920: abort (abort.c:79)
==67469==    by 0x578C966: __libc_message (libc_fatal.c:181)
==67469==    by 0x5837B8E: __fortify_fail_abort (fortify_fail.c:33)
==67469==    by 0x5837BB0: __fortify_fail (fortify_fail.c:44)
==67469==    by 0x583589F: __chk_fail (chk_fail.c:28)
==67469==    by 0x5837A99: __fdelt_chk (fdelt_chk.c:25)
==67469==    by 0x7272144: waitforclient (vpcd.c:186)
==67469==    by 0x7272641: vicc_connect (vpcd.c:369)
==67469==    by 0x72726D9: vicc_present (vpcd.c:390)
==67469==    by 0x7271F75: IFDHICCPresence (ifd-vpcd.c:428)
==67469==    by 0x10F492: ??? (in /usr/sbin/pcscd)
==67469== 
==67469== HEAP SUMMARY:
==67469==     in use at exit: 142,745 bytes in 1,314 blocks
==67469==   total heap usage: 4,099 allocs, 2,785 frees, 569,601 bytes allocated
==67469== 
==67469== LEAK SUMMARY:
==67469==    definitely lost: 116 bytes in 1 blocks
==67469==    indirectly lost: 0 bytes in 0 blocks
==67469==      possibly lost: 917 bytes in 4 blocks
==67469==    still reachable: 141,712 bytes in 1,309 blocks
==67469==         suppressed: 0 bytes in 0 blocks
==67469== Rerun with --leak-check=full to see details of leaked memory
==67469== 
==67469== For counts of detected and suppressed errors, rerun with: -v
==67469== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Aborted
bowb commented 3 years ago

My system has too many open sockets for FD_SET to work in vpcd.c The recommendation is to move to poll.

I changed it to the following, but I don't know if this will work on windows.

SOCKET waitforclient(SOCKET server, long secs, long usecs)
{
    struct pollfd pfd;
    struct sockaddr_in client_sockaddr;
    socklen_t client_socklen = sizeof client_sockaddr;
    int timeout;

    pfd.fd = server;
    timeout = (secs * 1000000 + usecs / 1000);

    if (poll(&pfd, 1, timeout) == -1)
        return INVALID_SOCKET; 

    if(pfd.revents & POLLIN)
        return accept(server, (struct sockaddr *) &client_sockaddr,
                &client_socklen);

    return INVALID_SOCKET;
}
bowb commented 3 years ago

Possible patch? I really don't know if the FD_SET on windows suffers from the same behavior on linux.

diff --git a/virtualsmartcard/src/vpcd/vpcd.c b/virtualsmartcard/src/vpcd/vpcd.c
index ef7bb85..da4a861 100644
--- a/virtualsmartcard/src/vpcd/vpcd.c
+++ b/virtualsmartcard/src/vpcd/vpcd.c
@@ -38,6 +38,7 @@ typedef WORD uint16_t;
 #else
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <poll.h>
 #include <stdint.h>
 #include <sys/socket.h>
 #include <sys/time.h>
@@ -171,21 +172,18 @@ err:

 SOCKET waitforclient(SOCKET server, long secs, long usecs)
 {
-    fd_set rfds;
+    
     struct sockaddr_in client_sockaddr;
     socklen_t client_socklen = sizeof client_sockaddr;
+   
+#if _WIN32
+    fd_set rfds;
     struct timeval tv;

-    FD_ZERO(&rfds);
-#if _WIN32
-    /* work around clumsy define of FD_SET in winsock2.h */
 #pragma warning(disable:4127)
     FD_SET(server, &rfds);
-#pragma warning(default:4127)
-#else
-    FD_SET(server, &rfds);
-#endif
-
+#pragma warning(default:4127)   
+ 
     tv.tv_sec = secs;
     tv.tv_usec = usecs;

@@ -193,6 +191,21 @@ SOCKET waitforclient(SOCKET server, long secs, long usecs)
         return INVALID_SOCKET;

     if (FD_ISSET(server, &rfds))
+    /* work around clumsy define of FD_SET in winsock2.h */
+
+#else
+    int timeout;
+    struct pollfd pfd;
+
+    pfd.fd = server;
+
+    timeout = (secs * 1000000 + usecs / 1000);
+
+    if (poll(&pfd, 1, timeout) == -1)
+        return INVALID_SOCKET; 
+
+    if(pfd.revents & POLLIN)
+#endif
         return accept(server, (struct sockaddr *) &client_sockaddr,
                 &client_socklen);
frankmorgner commented 3 years ago

in terms of portability, poll() should be good enough. Would you mind creating a pull request?