9

I recently managed to make fingerprint authentication work on my laptop, thanks to the Goodix Linux Development discord community and the github users Infinytum and Michael Teuscher on the driver/538d branch on https://github.com/infinytum/libfprint/tree/driver/538d.

But after enabling fingerprint for login with fprintd-enroll and for sudo access with sudo pam-auth-update I noticed a problem:

Whenever I am using an external monitor to work, and the lid is closed, I have to wait for the fingerprint authentication to time out, before being able to type the sudo password, which causes a delay of several seconds:

username@host:~/ron/libfprint$ sudo ls
Place your finger on the fingerprint reader
Place your finger on the reader again
Place your finger on the reader again
Place your finger on the reader again
Verification timed out
[sudo] password for username:

How can I disable fingerprint authentication when the laptop lid is closed, so I can type the sudo password without waiting for the fingerprint to time out?

user000001
  • 3,347
  • 19
  • 30

2 Answers2

11

To disable fingerprint authentication when the laptop lid is closed, and re-enable when it is reopened, we will use acpid to bind to the button/lid.* event to a custom script that will stop and mask the fprintd service on lid close, and unmask and start the fprintd service on lid open.

We also check that the HDMI cable is connected by testing the contents of /sys/class/drm/card0-HDMI-A-1/status.

Follow the steps below:

  1. Create file /etc/acpi/laptop-lid.sh with the following contents:

    #!/bin/bash
    
    lock=$HOME/fprint-disabled
    
    if grep -Fq closed /proc/acpi/button/lid/LID0/state &&
       grep -Fxq connected /sys/class/drm/card0-HDMI-A-1/status
    then
      touch "$lock"
      systemctl stop fprintd
      systemctl mask fprintd
    elif [ -f "$lock" ]
    then
      systemctl unmask fprintd
      systemctl start fprintd
      rm "$lock"
    fi
    
    
  2. Make the file executable with

    chmod +x /etc/acpi/laptop-lid.sh
    
  3. Create file /etc/acpi/events/laptop-lid with the following contents:

    event=button/lid.*
    action=/etc/acpi/laptop-lid.sh
    
  4. Restart the acpid service with:

    sudo service acpid restart
    

Now the fingerprint will be used only when the lid is open.

In order to restore the correct state of the fprintd service if you disconnect/reconnect while the laptop is off, you may call the above script from a systemd init file. The steps to do this are the following:

  1. Create a file named /etc/systemd/system/laptop-lid.service with the following contents:

    [Unit]
    Description=Laptop Lid
    After=suspend.target
    
    [Service]
    ExecStart=/etc/acpi/laptop-lid.sh
    
    [Install]
    WantedBy=multi-user.target
    WantedBy=suspend.target
    
  2. Reload the systemd config files with

    sudo systemctl daemon-reload
    
  3. Start the service with

    sudo systemctl start laptop-lid.service
    
  4. Enable the service so that it starts automatically on boot

    sudo systemctl enable laptop-lid.service
    

Now the status should be correct even after connecting/disconnecting when the computer is off.

References used for creating the code in the answer:

user000001
  • 3,347
  • 19
  • 30
  • 1
    This is a self-answered question, because I though it would be useful to share with other community members that may be facing the same problem. But I am interested in better/more efficient solutions, so feel free to post more answers and/or suggest improvements on this answer. – user000001 Nov 22 '21 at 12:53
  • 1
    Thanks, this is exactly what I was looking for. Works like a charm for my (docked) XPS 13 9310! – Benjamin Barrois Feb 07 '22 at 16:32
  • You're welcome @BenjaminBarrois, I updated the script with some changes that I made, so that it avoids some race conditions, and also activates only when the HDMI cable is connected. – user000001 Feb 07 '22 at 18:42
  • I'm having a problem when I switch my computer off while lid closed and start it back with lid open. When I do that, then I see no user on Ubuntu's login screen, I think it is because of the fprintd service remaining unstarted. Maybe there should be some actions triggered at startup to correct that? – Benjamin Barrois Feb 21 '22 at 15:52
  • @BenjaminBarrois: See updated answer, I forgot to include that initially. – user000001 Feb 21 '22 at 16:04
  • 1
    Great! This is exactly the system I used to have to change my CPU governor when plugging in or out my AC adpater. – Benjamin Barrois Feb 22 '22 at 14:32
  • 1
    My machine has a displayport not an HDMI so I needed to use ``` /sys/class/drm/card0-DP-1/status``` instead. Amazing post though - thank you!! – Richard Vodden Jun 06 '22 at 14:17
3

Fingerprint auth in Linux works through PAM, there is pam_fprintd.so module which talks to fprintd service. When fprintd service isn't working, pam_fprintd fails to communicate with it, and PAM auth skips to the next module in the config. But another PAM module could also skip over pam_fprintd.

So suppose you have /etc/pam.d/system-auth with the following contents:

auth            sufficient      pam_fprintd.so
auth            required        pam_unix.so

Your real config could be different or even in a different location, but what's crucial is pam_fprintd and pam_unix, which does password auth.

What you could do is add another entry into config, just before pam_fprintd.so which would skip that module if LID is closed. One way to achieve this is via pam_exec.so module. Create an executable script /usr/local/bin/pam_check_lid with the following contents:

#!/bin/sh
LID_STATE=$(cat /proc/acpi/button/lid/LID/state | cut -d':' -f2 | tr -d ' ')

case ${LID_STATE} in
    closed)
    echo closed
    exit 1
    ;;
    open*)
    echo open
    exit 0
    ;;
    *)
    # LID is open by default
    echo unknown
    exit 0
    ;;
esac

IMPORTANT: ensure that this file isn't globally writable. Otherwise, you might introduce a security vulnerability into your system. Also, you can check LID status over dbus, for details check this.

And then add the following line, just before pam_fprintd:

auth   [success=ignore default=1]   pam_exec.so quiet /usr/local/bin/pam_check_lid

So your config would look like this:

auth   [success=ignore default=1]  pam_exec.so quiet /usr/local/bin/pam_check_lid
auth   sufficient                  pam_fprintd.so
auth   required                    pam_unix.so

Now PAM auth would execute your script before attempting fingerprint auth, and if your script returns non-zero exit code (fails) it would do the default action, which skips 1 entry in a PAM config (so it skips pam_fprintd to the next auth which is pam_unix). When LID is open, it will return 0 exit code, and on success it wouldn't do anything, because of the ignore keyword in a config.

Alternatively, instead of pam_exec.so with a script, you can make your own PAM module with the same behavior as a script.

Few notes on debugging and actually implementing this. If you're having issues with pam_exec, you can add debugging options like debug and log, for details check pam_exec documentation. It is also good to test your changes on a new PAM config, instead of modifying an existing one, since a mistake in a config might lock you from your account. For example, you can just copy your system-auth as system-auth-new and work on system-auth-new and then replace it when it's tested. And for testing you could use pamtester. For example:

cp /etc/pam.d/system-auth /etc/pam.d/system-auth-new
# UPDATE /etc/pam.d/system-auth-new
pamtester system-auth-new YOUR_USER_NAME authenticate

And when it works as intended, replace system-auth with your new config.