September 8, 2013
So, my blogging has started to peter out again and in the interest of preventing that from happening, I decided to do another smartcard post.
I have a few smartcards: an old cyberflex e-gate card, a couple of Gemalto GemPlus java cards, and a military-style Common Access Card (CAC). When developing, I usually only use one of the gemalto tokens, because the e-gate card is obsolete and no longer supported in Fedora, while the Common Access Card is touchy and will eat itself as a security measure if it’s accessed the wrong way too many times.
Sometimes, though, it’s nice to be able to do testing without having to constantly plug and unplug a card. I used to do this using usb passthrough from a smartcard attached to the host to a qemu guest and then simulate insertion and removal via the qemu command console.
Recently, I discovered a better way by using virt-viewer and the SPICE protocol. It has the ability to emulate a Common Access Card in the guest merely by using certificates generated on the host. No smartcard required. This post is going to talk about the details of that.
Note, smartcards usually have one certificate for encryption and one certificate for signatures. There are no hard and fast rules, though. A card can, in theory, have a bunch of certificates. In addition to the aforementioned encryption and signing certificates, CAC cards also have another certificate called an “ID certificate”. The U.S. military assigns a number for every person signed up, called the EDIPI. It’s essentially a UUID used as an identifier used by various services. CAC cards store this number in the third certificate. It’s normally not used for authentication (though it actually can be used for authentication given the right configuration). The only reason I’m bringing up the ID certificate is because virt-viewer needs 3 certificates to emulate a CAC card, and I wanted to explain why it’s common to find 3 certs on CAC cards.
Before we jump to the certificate setup, though, we need to get the guest in order. First step is to shut the guest down and add a “smartcard” device in virt-manager and set that device to “passthrough” mode. This change allows the guest to create the virtual smartcard when virt-viewer passes along the right information. That information, is of course, the 3 certs I mentioned above.
So to generate the certs, we first, need to create a place to store the certificates.
host$ mkdir fake-smartcard
host$ cd fake-smartcard
host$ certutil -N -d sql:$PWD
At this point you’ll be asked for a password. This password becomes the smartcard PIN code on the guest system.
You could also just use the system NSS database (/etc/pki/nssdb) on the host directly, but then the smartcard would have a blank PIN (unless you put a password on the system NSS database).
The next step is to generate a CA cert on the host:
host$ certutil -S -s "CN=Fake Smart Card CA" -x -t "TC,TC,TC" -n fake-smartcard-ca -d sql:$PWD
The CA certificate is a toplevel certificate that the other certificates can chain up to. In a real deployment, a group of provisioned smartcards would have one common CA certificate. That CA certificate gets imported on the machines that allow that group of smartcards access. A machine can look at the smartcard, link it back to the CA certificate and know that that smartcard is trusted and allowed. This prevents having to import every certificate from every smartcard onto every machine. Instead, just the one CA certificate can be imported and the rest of the certificates are implicitly trusted by association with the CA certificate. Of course, in our situation it doesn’t matter much. We could just generate the three certificates without using a CA cert and import each one manually, but that’s neither here nor there.
So, now that we have the CA cert from the host, we need to export it to a file for the guest:
host$ certutil -L -r -d sql:$PWD -n fake-smartcard-ca -o fake-smartcard-ca.cer
and transfer the file to the guest and import it:
guest# certutil -A -d /etc/pki/nssdb -i fake-smartcard-ca.cer -n fake-smartcard-ca -t TC,TC,TC
Once we have a CA certificate we can generate the three certs we need to emulate a CAC card on the host:
host$ certutil -d sql:$PWD -t ",," -S -s "CN=John Doe (encryption)" --nsCertType sslClient -n encryption-cert -c fake-smartcard-ca
host$ certutil -d sql:$PWD -t ",," -S -s "CN=John Doe (signing)" --nsCertType smime -n signing-cert -c fake-smartcard-ca
host$ certutil -d sql:$PWD -t ",," -S -s "CN=John Doe" -n id-cert -c fake-smartcard-ca
(of course change John Doe above to your own name)
Once they’re made we just need to tell virt-viewer to pass them through to the guest via the virtual smartcard device:
host$ virt-viewer -c qemu:///system guestnamehere --spice-smartcard --spice-smartcard-certificates=id-cert,signing-cert,encryption-cert --spice-smartcard-db=sql:$PWD
The order of the passed in certs is important. It always goes ID, Signature, and then Encryption.
At this point the guest has a virtual smartcard. It can be “inserted” and “removed” by hitting shift-F8 and shift-F9, respectively (make sure you have the outer virt-viewer app focused and not the guest inside virt-viewer).
You can verify everything is working by first getting a list of certs:
guest# certutil -d /etc/pki/nssdb -L -h all
It should ask for your smartcard PIN (which is the password you assigned to the certificate database early on), and then show you all three certs and the manually imported CA cert.
You can look at the individual certs, too, and make sure they match what you generated on the host (e.g.):
guest# certutil -d /etc/pki/nssdb -L -n "John Doe:CAC Email Encryption Certificate"
If pam_pkcs11 is installed and configured, the smartcard should work for login as well. By default it will associate with the user that has “John Doe” in the GECOS field of the password file. There are other ways to configure how pam_pkcs11 maps the card to a user account, too, but I won’t get into that here.
This kind of setup isn’t appropriate for every situation, but it is still very useful, in a lot of cases, for quickly doing smartcard related development/testing.
(EDIT: if you follow these instructions please note this bug where invalid PINs can be accepted: http://lists.nongnu.org/archive/html/qemu-devel/2013-09/msg01365.html )