# Two-factor login

Do you still type a password to get into your laptop? What about in the office? Or when you're on the train? What if there are cameras above your head? Have you ever worried that a snooper might get your password and do bad things with it?

I'm sure you haven't. Like a properly security-aware hacker, you disabled password-based SSH login already. You also don't reuse your laptop's password for anything else, especially not for your backups. Even if someone snoops the password, all they get is access to the laptop itself. Right?

Haha, I betcha. Let's face it: opsec is hard. Upgrading your opsec skill takes effort. Good opsec reduces the likelihood that secrets will be spilled.

That's why I decided to augment my laptop with two-factor login.

Now, I can whip out my laptop on the train, and log in to it without worrying too much whether the password gets intercepted. It's unique, so the attacker won't hack any of my other properties (and I'm not leaving my laptop sitting around). I see you grumble: "another unique password is hard to remember! What's the point if you could just change your login password?" The hardware token makes short and memorable passwords possible, because it bricks itself after 3 failed guesses! So I can get away with a password that's really easy to remember, without losing resistance to brute forcing.

Now I can also leave the laptop in a public space for 5 minutes. Even the security guard who used the ceiling camera to record me typing the password won't get into my laptop undetected. After all, I took the hardware token with half of my login credentials with me!

**WARNING:** I only cover the user login procedure in this guide. Not unlocking the disk encryption. The evil security guard can still compromise the laptop by watching me enter the decryption password and then rebooting the system. Stealing the laptop is not in my threat model for this blog post.

## Second factor

Two-factor authentication splits your secret into pieces of two different types (factors), which have different security properties. This is to reduce the likelihood of the entire secret getting intercepted. Each factor may get intercepted easily on its own, but both at the same time is going to be much more difficult because of how they differ.

There are two most common factors: "something you know", like a password, and "something you have", like a hardware token. Passwords can be intercepted by watching someone type them, or by key logging, or by retrieving it from badly secured storage. A hardware token cannot be duplicated, and its secret can only be intercepted by physically stealing the token.

While hardware tokens contain some data constituting the secret, it's not possible to get that data out. If you *can* duplicate the secret, then the token not doing its basic job as a "something you have", and stops being a second factor to a password. It basically becomes a password with extra steps.

## Login setup

This guide will show you how to enable 2-factor authentication with a password and a smart card (Nitrokey Pro) on Fedora 37. It does not disable the original password, so that locking yourself out is harder.

First, install some packages:

sudo dnf install sssd opensc p11-kit gnutls-utils nitrokey-app easy-rsa

Let's play around with the smart card itself. Plug it into the USB port and give it a go. First, run *nitrokey-app* and set up the user and administrator passwords (if you have trouble finding the options, check out the menu bar).

Now, play around with the key.

$ p11tool --list-tokens

Token 2:
        URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=xxxxxx;token=OpenPGP%20card%20%28User%20PIN%29
        Label: OpenPGP card (User PIN)
        Type: Hardware token
        Flags: RNG, Requires login
        Manufacturer: ZeitControl
        Model: PKCS#15 emulated
        Serial: 000500005c61
        Module: opensc-pkcs11.so

Token 3:
        URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=xxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29
        Label: OpenPGP card (User PIN (sig))
        Type: Hardware token
        Flags: RNG, Requires login
        Manufacturer: ZeitControl
        Model: PKCS#15 emulated
        Serial: 000500005c61
        Module: opensc-pkcs11.so

Remember this command, cause the URL field will be needed later.

### Certificates

The login procedure involves checking PKCS12 certificates, so let's set up one using easy-rsa. First, create a file called "vars" in some directory, and put this inside:

set_var EASYRSA_KEY_SIZE 4096

Now, let's create the entire certificate chain for the user "thatsme". It will ask you for different passwords: one for the certificate authority (you only need to use it when adding another certificate), and one for the private key (you'll need it once, when uploading the private key to the smart card), so pay attention to them.

ln -s /usr/share/easy-rsa/3.0.8/ easyrsa
EASYRSA_VARS_FILE=`pwd`/vars ./easyrsa/easyrsa init-pki
EASYRSA_VARS_FILE=`pwd`/vars ./easyrsa/easyrsa build-ca
EASYRSA_VARS_FILE=`pwd`/vars ./easyrsa/easyrsa build-client-full thatsme

Whew, this wasn't as difficult as the last time I tried to set up a new certificate authority. Good job, easy-rsa folks!

Now, store the generated private key and the corresponding certificate (approved by the newly minted certificate authority) into the smart card.

**WARNING:** we generated a private key on the host computer. It can be intercepted by a rogue application on this computer, especially if you don't delete it fully (and deleting is harder than it seems). The interceptor will be able to follow the next steps to duplicate your smart card. If you want to be super careful, you should generate the key directly on the smart card, and use Certificate Signing Requests (CSRs) with easy-rsa to approve the key. This is not relevant to me, because I not will use the smart card to log in to any other computer. If someone snooped the key from the laptop, I'm already hosed. So I chose the easy way to generate the key.

pkcs15-init --delete-objects privkey,pubkey --id 3 --store-private-key ./pki/private/thatsme.key --format pem --auth-id 3 --verify-pin
pkcs15-init --id 3 --store-certificate ./pki/issued/thatsme.crt

See how snugly the key and the certificate both sit on your smart card now? Use the URL you noted earlier to show them.

$ p11tool --provider /usr/lib64/opensc-pkcs11.so --list-all pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=xxxx;token=OpenPGP%20card%20%28User%20PIN%29
Object 0:
        URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=xxxx;token=OpenPGP%20card%20%28User%20PIN%29;id=%03;object=Authentication%20key;type=public
        Type: Public key (RSA-4096)
        Label: Authentication key
        ID: 03

Object 1:
        URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=xxxx;token=OpenPGP%20card%20%28User%20PIN%29;id=%03;object=Cardholder%20certificate;type=cert
        Type: X.509 Certificate (RSA-4096)
        Expires: Sun Sep 28 09:07:36 2025
        Label: Cardholder certificate
        ID: 03

I tried listing the private keys, but for some reason, p11tool won't display any.

## Login

Now that the card is prepared, let's set up the login. First, copy the certificate authority to where sssd can find it. Any key signed with this certificate will be candidate for login.

sudo cp pki/ca.crt /etc/sssd/pki/sssd_auth_ca_db.pem
sudo chmod ugo-w /etc/sssd/pki/sssd_auth_ca_db.pem

Then, set up sssd itself by creating the file `/etc/sssd/conf.d/base.conf` with the following contents:

services = nss, pam
domains = files
disable_netlink = true

id_provider = files

pam_cert_auth = True
pam_p11_allowed_services = +kde, +kscreensaver, +sddm

matchrule = <SUBJECT>^CN=thatsme$

*NOTE:* The common name (file name) given to easy-rsa must match the username on this system. If anyone knows how to break that relationship, please tell me! I know it has to do with sss-certmap, but the docs are incomprehensible to me.

Finally, enable sssd. Do this as root:

systemctl enable sssd
authselect select sssd with-smartcard --force
systemctl restart sssd

And that's it. When the smart card is plugged in, you should be able to see this:

$ sudo echo "ok"
PIN for OpenPGP card (User PIN):

rather than the normal

$ sudo echo "ok"

Similarly, the KDE screen locker will accept the smart card password rather than the user password. It doesn't indicate that this is happening though, other than being really slow with the card in.

### Debugging

Enable logging by adding the `debug_level` entry:

pam_cert_auth = True
debug_level = 9

and restarting sssd.

The two relevant log files are here:




but they are not that helpful, because sssd documentation doesn't really explain a lot.

Good luck upgrading your opsec!

## Actually, no login

I did all of that, I rebooted my computer, I plugged in the Nitrokey, and I entered my password at the SDDM login screen. The prompt takes some long seconds to resolve – good, it's working with the card.

> Login refused

Wait, what? I try again, same result. I try my regular password, and that goes through.

The logs in /var/log/sssd/sssd_pam.log show:

[pam_reply] (0x4000): [CID#3] pam_reply initially called with result [9]: Authentication service cannot retrieve authentication info. this result might be changed during processing
[pam_reply] (0x0200): [CID#3] blen: 22
[pam_reply] (0x0200): [CID#3] Returning [9]: Authentication service cannot retrieve authentication info to the client
[client_recv] (0x0200): [CID#3] Client disconnected!

Meanwhile, a successful use in sudo:

[pam_reply] (0x4000): [CID#9] pam_reply initially called with result [9]: Authentication service cannot retrieve authentication info. this result might be changed during processing
[pam_reply] (0x0040): [CID#9] Assuming offline authentication setting status for pam call 249 to PAM_SUCCESS.
[pam_eval_prompting_config] (0x4000): [CID#9] No prompting configuration found.
[may_do_passkey_auth] (0x0400): [CID#9] Passkey auth not possible, SSSD built without passkey support!
[pam_reply] (0x0200): [CID#9] blen: 22
[pam_reply] (0x0200): [CID#9] Returning [0]: Success to the client

I don't know if this difference is relevant. My search engine skills failed me, and I eventually gave up.

I guess I'll just have to use the main password for login. Thankfully, when traveling, I resume the laptop from sleep, and the screen locker accepts the smart card password, so it's not that bad.

Either way, if you know how to solve this problem (any sddm devs around?), please let me know! I'll update this post with your info.

Thanks to one of my coworkers at Purism, for showing me that not every password must be guarded jealously.

Written on .


dcz's projects

Thoughts on software and society.

Atom feed