✅ Linux Fingerprint and/or Biometric support

Thanks all, your feedback has been passed along to the team :+1:

3 Likes

I did some triage to see what’s necessary to implement this. The best way to do this on linux is to simply talk to PAM. PAM has modules for all kinds of authentication including libfprint (fingerprint readers) and howdy (windows hello-like camera unlock).

The desktop app has native rust modules already, so the steps would boil down to adding a PAM policy for bitwarden’s biometrics (not sure how the different package managers Bitwarden distributes to handle this), and writing a module using the pam-client crate.

Some sample code to use pam from rust (tested with fingerprint):

use pam_client::{Context, Flag};
use pam_client::conv_cli::Conversation;

fn main() {
    let mut context =Context::new(
    "bitwarden", 
    None,
    Conversation::new()
    ).expect("Failed to initialize PAM context");

    let result = context.authenticate(Flag::NONE);
    if result.is_err() {
        println!("Authentication failed");
        println!("Error: {:?}", result.err().unwrap());
        return;
    } else {
        println!("Authentication succeeded");
    }
}

And the /etc/pam.d/bitwarden could look like this:

#
# PAM configuration for bitwarden
#
auth sufficient pam_fprintd.so
6 Likes

As a sidenote, pam could also be used for a fallback user password login (which could also be achieved using polkit). I do agree on the fact that this would be an awesome feature to see implemented. Let’s hope for this to happen eventually :slight_smile:

Built a prototype for this based on my sample above. Unlocking with fprintd works, but it’s still missing some other stuff.

3 Likes

@Quexten that is some great work!

Installing the correct PAM policy on install
I don’t think this is 100% necessary, as installing fprint often requires some manual configuration for many things (e.g. sudo) anyway, and while it would be nice it shouldn’t be considered a priority.

I will test this out. Again, this would be a great improvenet, thanks a lot!

Do you have any idea of when this might be ready? Is there any way I can be helpful for testing?

Also, how does this work with the browser integration (browser extension)

Yeah, maybe. I think a default policy would be nice, that at least has basic fprintd support. If the user then wants howdy / other PAM stuff the could do this themselves.

1 Like

Like I would maybe do for this “gross hack”, which would be useless tho if there was no password as fallback (which should already be there, just outside of pam’s control. Also, I don’t think I’m getting your fork to compile correctly (not your fault, but mine, I don’t even know how to with the mainline).

Would you mind giving any instructions? npm ci doesn’t seem to work…

If the rust module has trouble building, It’s a known issue. Compile with:

 source ~/.cargo/env                                                                                                                                              
    export PKG_CONFIG_ALL_STATIC=1
    export PKG_CONFIG_ALLOW_CROSS=1
    rustup target add x86_64-unknown-linux-musl
    npm run build -- --target x86_64-unknown-linux-musl

Aside from that it should just be npm install , npm run electron

Not sure i see what the “gross hack” is for. The example pam policy I posted only does fprintd, no passwords. So it basically is just accessing fprintd through pam. Am I misunderstanding what it is for?

As posted on the Pull Request forum post on this issue, the pull request lives here:

Switched from using PAM directly to Polkit now since that seems better integrated into most systems. Using the KDE Polkit agent asks me for both fingerprint and password at the same time. If you don’t have any biometrics configured it’s authenticating with password.

Hi, sorry for not answering sooner.

Would you mind sharing a more detailed way to compile and run this? Eventually, specifying the specific directory I should be in.

Also, I also agree that using Polkit would be a cleaner way than directly calling fprintd, but in that case it would probably be appropriate to make the option show up as “System Authentication” or something similar, when running. An other potential issue might be that using polkit would be decently slower than calling fprintd directly, but that should be negligible.

Sure, I’ll give a write-up of how to build in a bit (tomorrow probably).
About performance: The window for authentication opens pretty much instantly, after that it just takes as long as the user takes to authenticate (facial detection / fprintd / entering password). So in real world use there is no difference.

Also it shows up as “Polkit” in the settings / UI not biometrics / system authentication.

Instructions to build:

git clone -b "feature/unix-biometrics" [email protected]:quexten/clients.git
cd clients 
npm install
cd apps/desktop 
npm install
cd desktop_native
source ~/.cargo/env    
export PKG_CONFIG_ALL_STATIC=1
export PKG_CONFIG_ALLOW_CROSS=1
rustup target add x86_64-unknown-linux-musl
npm run build -- --target x86_64-unknown-linux-musl
cd ..
npm run electron

Depending on your config the separate rust step might not be required, but this should get the dev build running on your system.

To use polkit, with bitwarden, you need to create a policy:

/usr/share/polkit-1/actions/com.bitwarden.Bitwarden.policy                                                                                                                


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">

<policyconfig>
    <action id="com.bitwarden.Bitwarden.unlock">
      <description>Unlock Bitwarden</description>
      <message>Authenticate to unlock Bitwarden</message>
      <defaults>
        <allow_any>auth_self</allow_any>
        <allow_inactive>auth_self</allow_inactive>
        <allow_active>auth_self</allow_active>
      </defaults>
    </action>
</policyconfig>

And if you want to use biometrics with polkit in general, you might need to adjust your PAM config for polkit:

/etc/pam.d/polkit-1                                                                                                                                                       


#%PAM-1.0

# Uncomment for howdy face detection
# auth sufficient pam_python.so /lib/security/howdy/pam.py
# Uncomment for fprintd 
# auth       sufficient   pam_fprintd.so
auth       include      system-auth
account    include      system-auth
password   include      system-auth
session    include      system-auth

If you run on some system without a default polkit agent (Arch btw) you need to also start the agent of your choice.

Thanks a lot. I already have polkit set up and with an agent (lxsession).

Also, I would edit your message to clone using https as ssh doesn’t seem to work for me

Anyway, Everything seems to work for me. Except for the browser integration (which I’m guessing you’re still working on?). The desktop clients never asks me for any authentication after I try to enable it in the browser, Despite theoretically turning it on inside of the settings.

I do, tough, still see some messages printed to stdout when enabling the browser integration:

[Elec] Server path not specified, so defaulting to ipc.config.socketRoot + ipc.config.appspace + ipc.config.id /tmp/app.bitwarden
[Elec] starting server on  /tmp/app.bitwarden 
[Elec] starting TLS server false
[Elec] starting server as Unix || Windows Socket

I’m guessing that this is expected (?)

Anyway, thanks a lot for the work you’re putting into this, it’s really appreciated!

Also, I would edit your message to clone using https as ssh doesn’t seem to work for me

This is because you don’t have your ssh keys registered at github. You can add them to your github account (if you have one) and then you can clone over ssh. Cloning over https also works of-course.

I’ve also seen the browser does not work, I have not debugged this yet though. It seems the browser ↔ desktop IPC does not work correctly on Linux.

Thanks for the suggestion, I will gladly add my keys to github.

Also, let me know whether you get anything with the browser integration working, I would be glad to test it out.

To give this a little more testing, which files should I move and to where to make the default /usr/share/applications/bitwarden.desktop launch this fork instead of just /usr/lib/bitwarden/app.asar (as the build directory doesn’t seem to contain such file. I’ve tried launching main.js using electron, but that just gives a non-working e-mail login screen). I do understand that this version might not be completely stable and is nothing like a commercial release, but that is, for my usecase, not an issue.

My best regards

The debug build relies on a running webpack server, I don’t think you can copy the debug build files and run them directly. in the [Project-Dir]/apps/desktop directory run:

npm run dist:lin

This will build all target platforms (snap/appimage/deb/rpm/etc.). From there you should just be able to run the AppImage which should install itself with a desktop file or whichever other package you want. But fair warning, I’m not necessarily keeping the branch up to date with master so you wouldn’t have the latest changes this way until Bitwarden merges the Pull-Request.

Also thanks for making me check this because I just noticed the AppImage seems to show a blank text on KDE agent (it still works though). Guess I have to check how Polkit interacts with the different sandboxes of AppImage/snap/others.

Interestingly, that does not seem to work (running in the dir you specified, at least):

[I’m sorry for the codewall]

[...]
## After sign
  • building        target=AppImage arch=x64 file=dist/Bitwarden-2023.1.2-x86_64.AppImage
  • building        target=snap arch=x64 file=dist/bitwarden_2023.1.2_amd64.snap
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/appimage-12.0.1/appimage-12.0.1.7z size=1.6 MB parts=1
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/snap-template-4.0-2/snap-template-electron-4.0-2-amd64.tar.7z size=1.5 MB parts=1
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/appimage-12.0.1/appimage-12.0.1.7z duration=1.016s
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/snap-template-4.0-2/snap-template-electron-4.0-2-amd64.tar.7z duration=1.047s
  • building        target=deb arch=x64 file=dist/Bitwarden-2023.1.2-amd64.deb
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/fpm-1.9.3-2.3.1-linux-x86_64/fpm-1.9.3-2.3.1-linux-x86_64.7z size=5.0 MB parts=1
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/fpm-1.9.3-2.3.1-linux-x86_64/fpm-1.9.3-2.3.1-linux-x86_64.7z duration=1.94s
  ⨯ cannot execute  cause=exit status 127
                    errorOut=/home/alba4k/.cache/electron-builder/fpm/fpm-1.9.3-2.3.1-linux-x86_64/lib/ruby/bin.real/ruby: error while loading shared libraries: libcrypt.so.1: cannot open shared object file: No such file or directory
    
                    command=/home/alba4k/.cache/electron-builder/fpm/fpm-1.9.3-2.3.1-linux-x86_64/fpm -s dir --force -t deb -d libnotify4 -d libxtst6 -d libnss3 -d libsecret-1-0 -d libxss1 --deb-recommends libappindicator3-1 --deb-compression xz --architecture amd64 --after-install /tmp/t-vm3ns6/0-after-install --after-remove /tmp/t-vm3ns6/1-after-remove --description 'A secure and free password manager for all of your devices.
     A secure and free password manager for all of your devices.' --version 2023.1.2 --package /home/alba4k/Scaricati/clients/apps/desktop/dist/Bitwarden-2023.1.2-amd64.deb --name bitwarden --maintainer 'Bitwarden Inc. <[email protected]>' --url https://bitwarden.com --vendor 'Bitwarden Inc. <[email protected]>' --deb-priority optional --license GPL-3.0 /home/alba4k/Scaricati/clients/apps/desktop/dist/linux-unpacked/=/opt/Bitwarden /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/16x16.png=/usr/share/icons/hicolor/16x16/apps/bitwarden.png /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/32x32.png=/usr/share/icons/hicolor/32x32/apps/bitwarden.png /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/64x64.png=/usr/share/icons/hicolor/64x64/apps/bitwarden.png /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/128x128.png=/usr/share/icons/hicolor/128x128/apps/bitwarden.png /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/256x256.png=/usr/share/icons/hicolor/256x256/apps/bitwarden.png /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/512x512.png=/usr/share/icons/hicolor/512x512/apps/bitwarden.png /home/alba4k/Scaricati/clients/apps/desktop/resources/icons/1024x1024.png=/usr/share/icons/hicolor/1024x1024/apps/bitwarden.png /tmp/t-vm3ns6/6-Bitwarden.desktop=/usr/share/applications/bitwarden.desktop
                    workingDir=
npm ERR! Lifecycle script `pack:lin` failed with error: 
npm ERR! Error: command failed 
npm ERR!   in workspace: @bitwarden/[email protected] 
npm ERR!   at location: /home/alba4k/Scaricati/clients/apps/desktop 
npm ERR! Lifecycle script `dist:lin` failed with error: 
npm ERR! Error: command failed 
npm ERR!   in workspace: @bitwarden/[email protected] 
npm ERR!   at location: /home/alba4k/Scaricati/clients/apps/desktop 
Interestingly, that does not seem to work (running in the dir you specified, at least):

[I’m sorry for the codewall]

Seems to be you are missing libcrypt. Depending on your distro you’ll have to install that. On arch it’s the libxcrypt-compat package f.e.

Also thanks for making me check this because I just noticed the AppImage seems to show a blank text on KDE agent (it still works though). Guess I have to check how Polkit interacts with the different sandboxes of AppImage/snap/others.

Seems my kde polkit agent was just broken. After restarting it, things worked fine again, even in appimage.

Also, let me know whether you get anything with the browser integration working, I would be glad to test it out.

I had to learn about how the native messaging between browsers and the app even works. Seems out there wasn’t an issue in the ipc code regarding linux. It’s just that it doesn’t work in the debug build.
From what I found it will also not work under snap and flatpak, due to how they are sandboxed.

With appimage it works just fine. So if you have the appimage built, delete the following file:
.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json
or
.mozilla/native-messaging-hosts/com.8bit.bitwarden.json
for Firefox.

Then start the AppImage, and re-enable biometrics support. Then native messaging, and thus the browser extension should work (verified on chrome and Firefox on my system).

Hi, sorry for the late response.

I’m getting the same error for both the AppImage and the linux executable

A JavaScript error occurred in the main process
Uncaught Exception:
Error: Cannot find module '@bitwarden/desktop-native-linux-x64-musl'
Require stack:
- /home/alba4k/Scaricati/clients/apps/desktop/dist/linux-unpacked/resources/app.asar/node_modules/@bitwarden/desktop-native/index.js
- /home/alba4k/Scaricati/clients/apps/desktop/dist/linux-unpacked/resources/app.asar/main.js
- 
    at Module._resolveFilename (node:internal/modules/cjs/loader:940:15)
    at n._resolveFilename (node:electron/js2c/browser_init:249:1105)
    at Module._load (node:internal/modules/cjs/loader:785:27)
    at c._load (node:electron/js2c/asar_bundle:5:13339)
    at Module.require (node:internal/modules/cjs/loader:1012:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/home/alba4k/Scaricati/clients/apps/desktop/dist/linux-unpacked/resources/app.asar/node_modules/@bitwarden/desktop-native/index.js:160:29)
    at Module._compile (node:internal/modules/cjs/loader:1120:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1175:10)
    at Module.load (node:internal/modules/cjs/loader:988:32)