clients
clients copied to clipboard
[PM-990] Unix biometrics unlock via Polkit
đī¸ Tracking
https://bitwarden.atlassian.net/browse/PM-990
đ Objective
Implements biometrics / "system authentication" via polkit over dbus. Since there is no local authentication portal (https://github.com/flatpak/xdg-desktop-portal/discussions/1275) yet, this requires manual (or automatic) setup of polkit policies with admin privileges. Setup is done automatically where possible, and users are directed to a guide when needed with warning messages. Since we do not have secure persistent storage available on linux, we only enable this after first unlock.
NOTE: This does not yet fix browser integration for snap/flatpak installations. This can also be done, but requires re-thinking the setup a bit. A manual setup step placing a script called by the browser extension, which dynamically looks up the installation type and calls it, would fix this. But that is out of scope.
May 2024 update since the PR was stale fore quite a while, brought it up to date, with automatic setup, installation detection, and manual setup instructions for sandboxed installations.
Code
In the rust code, zbus is added as a dependency to interact with dbus/polkit. We eventually need this either way if we want to interact with other dbus interfaces (idle/lock screen on gnome). Further, since zbus calls need to be async, the biometric trait needs to be be converted to use async traits using the async_trait crate.
Screenshots
â° Reminders before review
- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team
đĻŽ Reviewer guidelines
- đ (
:+1:) or similar for great changes - đ (
:memo:) or âšī¸ (:information_source:) for notes or general info - â (
:question:) for questions - đ¤ (
:thinking:) or đ (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion - đ¨ (
:art:) for suggestions / improvements - â (
:x:) or â ī¸ (:warning:) for more significant problems or concerns needing attention - đą (
:seedling:) or âģī¸ (:recycle:) for future improvements or indications of technical debt - â (
:pick:) for minor or nitpick changes
Thank you for your contribution! We've added this to our internal Community PR board for review. ID: PS-2370
Ok there seems to be an issue still with accounts that do not have biometrics enabled. On my first test account it works great, on my other 2 test accounts it does not work. Need to triage this.
Ok there seems to be an issue still with accounts that do not have biometrics enabled. On my first test account it works great, on my other 2 test accounts it does not work. Need to triage this.
Actually nevermind, it works just fine. The problem was that due to the lack of UI feedback with just fprintd, I didn't realize I had to authenticate my fingerprint to enable the setting.
Hmm, we could also use polkit instead of PAM directly. This would then be system level authentication, instead of biometrics directly. If the user has biometrics configured, that will be used, if not it will prompt for system password. Also this has native GUIs on most desktop environments so it would be a more integrated experience.
Hmm, we could also use polkit instead of PAM directly. This would then be system level authentication, instead of biometrics directly. If the user has biometrics configured, that will be used, if not it will prompt for system password. Also this has native GUIs on most desktop environments so it would be a more integrated experience.
Yeah I tested with kde polkit agent and gnome polkit agent and they provide a better experience. I will have to update this PR to use polkit.

Implemented polkit instead of pam, this is what it looks like with KDE's polkit agent.
cat /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>
This is the polkit policy that needs to be installed for it to work. I'm not entirely sure how to best do this with electron builder, didn't find a build-in function for this. Maybe someone from the team can share some insight?
On some platforms we could use the afterInstall options / afterRemove options, and on the others just disable it and document for the user to set up this polkit policy themselves if they want this feature to work?
One note: Biometrics for browser extensions will not work if:
- The browser is installed as snap or flatpak.
- The Bitwarden Desktop app is installed as snap or flatpak
This is unrelated to this pull request, but due to how native messaging works, and how flatpak/snap sandbox applications. There is an open pull request for xdg-desktop-portal support which might fix this: https://github.com/flatpak/xdg-desktop-portal/pull/705
Aside from that, I tested AppImage to correctly work with the desktop application, and I expect the same to be the case for the other formats which don't have special sandboxing.
Thank you so much for doing this!
Any plans on getting this merged?
+1
Well, from my side this is mostly good to go.
The only thing missing is a way to get the Polkit policy installed. I looked at electron builder's options for this but this will only work on some package formats. The electron builder documentation isn't entirely clear on this (it lists some, but not .deb for example, but then fpm supports .deb again). AppImage won't work at all using this method (and seems to be the default recommended installation option on the Bitwarden website).
Another option is to install this policy during runtime (when the user first activates biometrics). This would require elevating privileges with polkit, but should not be an issue.
I need input on which approach the Bitwarden team would prefer here.
On another note, since I didn't want to have the desktop app and IPC proxy constantly running in the background, I've re-implemented (a subset of) the protocol in a single go application. Other than that it works in the same way (biometrics with polkit, secret stored in the system's secret service). In case this is also useful to someone following this thread here is the link: https://github.com/quexten/bw-bio-handler
Let's hope this gets merged as soon as possible
Thank you for this, @quexten. Please know that I've had literally daily reminders to get information to you as soon as possible once the security work related to overhauling biometrics was done and merged.
That work changes the API around biometrics pretty significantly. Linux will have similar security model issues that Windows does in that security is entirely user-based. DBus in particular offers difficulties. Any application running as a user has unfettered access to messages -- that's kind of the point. Therefore, we need messages to be encrypted secrets rather than plaintext like there are here.
We can do that in at least one of two ways
- encryption by a libsecret challenge-derived password
- encryption by a secret stored in the bitwarden local data
I also don't know of any way to use polkit or libsecret to do an analogous user key signing we're doing for Windows -- do you?
If we can't find a way to do the same kind of challenge signing, we'll have to require an alternate unlock on app start and use the established client-key-half method as a non-optional requirement.
Thank you for the update on this @MGibson1!
That work changes the API around biometrics pretty significantly. Linux will have similar security model issues that Windows does in that security is entirely user-based. DBus in particular offers difficulties. Any application running as a user has unfettered access to messages -- that's kind of the point. Therefore, we need messages to be encrypted secrets rather than plaintext like there are here.
Yeah I was also concerned about this when writing the PR but did not want to introduce changes in how the secrets are stored/derived in this PR. I had a brief look and very much agree with the changes in your linked merged PR though.
I also don't know of any way to use polkit or libsecret to do an analogous user key signing we're doing for Windows -- do you?
No, polkit only checks whether a user is allowed to do something, libsecret does not protect against attacks from the user's session. So using them directly in the rust desktop library to do this is not possible as far as I can tell. That being said, I do have a suggestion of how to still implement this behaviour below.
Assuming I understand the requirements correctly, getting a symmetric encryption secret, not accessible by the user session, locked behind biometrics, there are two additional approaches I could think of:
-
There is a security model which is per-application on Linux: The Secret portal allows per application secrets to be retreived for sandboxed apps such as Flatpak (Snap?). Other sandboxed applications cannot access them. Although they are also only separated between sandboxed applications, but not from processes running un-sandboxed under the user. Biometrics would then still just return true/false, but the application can just retrieve it's secret. This would only apply to very specific Linux distros that make heavy use of Flatpaks, where the threat-model only includes other sandboxed applications. Given that Bitwarden is not even officially distributed as Flatpak, this is probably not the way to go, but it still felt relevant to mention here.
-
The better option in this case, which more closely resembles the new Windows behaviour, is to store the secret in a file inaccessible to the user in the app directory (owned by a new user or only readable by root with
chmod 000). To access the key, an executable would be invoked, that verifies the user's biometrics via polkit. The binary would have permission to read the secret via capabilities or the suid bit to elevate privileges and read and return the secret. This would prevent attacks from the user-session, and provide a secret protected by biometrics, but it would also be another (albeit small) binary to maintain.
Of course the approach you listed of requiring an alternative unlock method (the master password/pin) at least once on startup would also work and be the simpler one implementation wise.
Hi all,
Since we're already messing with system config (PAM initially, now polkit), another option could be to do something similar to pam_gnome_keyring.so (it tries to unlock the gnome keyring on login using the user input password)
We could implement something in the same vein to derive an encryption key from the user password and pass it to a daemonized bitwarden service.
This requires that the users opens its Desktop session with password (not biometrics), but this is already an issue with gnome keyring (if you log in with biometrics, you have to manually unlock your keyring with your password afterwards). I personally use pam_fprintd for everything but graphical login (session unlock, console login, sudo,...)
Another solution I would be fine with is to just warn the user that enabling bio unlock will lower the security.
Regards, Nicolas
Those are all interesting options.
The Secret portal allows per application secrets to be retreived for sandboxed apps such as Flatpak (Snap?).
I like this approach, but, as you point out, it's not sufficient for the same reasons Windows UWP isn't a solution. The threat model is from non-sandboxed applications and since it provides no sandboxing from those applications, it really only provides security for systems which exclusively run Flatpak. I don't know of any that have invested that far.
store the secret in a file inaccessible to the user in the app directory
This is an interesting approach, but I'm unsure about delivery. I'm not familiar enough with our distribution channels to know if we could have this separate executable bundled across all of them. We could rework the download experience to be an installer-type thing...
Is it possible to enable this kind of functionality by executing sub-processes from the electron main thread as a part of the native messaging library? I'm thinking the effect would be something like running.
sudo -u bitwarden cat ./biometric-signing.crt
Please forgive my ignorance here, this is all pretty exploratory for me.
pass it to a daemonized bitwarden service.
I'd like to avoid a daemonized process if we can. We could enable other great features (like getting rid of session tokens in CLI) that I've thought though in the past, but requiring one is also a barrier to entry that would be good to avoid.
I think the guaranteed way forward will be to wire up a non-optional require password on app start and eventually work in ways to expand our linux biometric feature offering as the above ideas develop.
If you're more interested in fleshing out the above ideas, totally cool. My point is that I'd also entertain a PR dedicated to just enabling biometric after first unlock on Linux.
@MGibson1
This is an interesting approach, but I'm unsure about delivery. I'm not familiar enough with our distribution channels to know if we could have this separate executable bundled across all of them.
This would be simple enough for native packages like DEB and RPM, but I don't think it's feasible for bundled and sandboxed versions like AppImage and Snap.
Aside: Is there an official Flatpak version of Bitwarden? I see com.bitwarden.desktop on Flathub, but I don't see it mentioned on the website at https://bitwarden.com/download/.
I think the guaranteed way forward will be to wire up a non-optional require password on app start and eventually work in ways to expand our linux biometric feature offering as the above ideas develop.
Yeah, agreed.
If you're more interested in fleshing out the above ideas, totally cool. My point is that I'd also entertain a PR dedicated to just enabling biometric after first unlock on Linux.
I'm OK with just making this PR only enable biometrics after first unlock. Improvements can come in a separate PR.
I did not yet check what's required to bring this PR up-to-date with the current master though.
Other than that, as mentioned above, only a way to install the polkit policy is missing.
Currently I'm inclined towards copying with something like pkexec cat bitwarden.policy .. during runtime when the user asks to enable biometrics, as this will work with all package formats.
Some more points on how biometric unlock could work without the additional unlock on boot requirement (for a separate PR):
Is it possible to enable this kind of functionality by executing sub-processes from the electron main thread as a part of the native messaging library? I'm thinking the effect would be something like running.
sudo -u bitwarden cat ./biometric-signing.crt
Yeah, that's the idea. Sudo works by being owned by root, and having the suid flag set. Thus the effective user id gets switched to root. In case of "-u bitwarden" sudo would then switch from root to the bitwarden user and execute cat. In this case it could then read the signing cert.
I don't believe we can just use sudo though, since this is missing the biometrics check (unless the user specifically configures pam modules), so we would have to enter the user's password in the app and pass that to sudo. If we wanted to just use existing executables, then pkexec --user bitwarden cat ... would be a more suitable approach as this uses polkit (and thus biometrics, if configured). Furthermore, the user would have to have permission to sudo, which might not be the case on locked down systems.
An issue here would be that the polkit message is unintuitive. Instead of something like "authenticate to unlock bitwarden", it is:

Compared to using a custom polkit policy (which is used in the current PR):

Given this, a small binary that is owned by the bitwarden user, and has the suid bit set would be a nicer solution as it doesn't require sudo permissions, and have a nice authentication message. In that case, the native messaging library would call something like: ./biometrics-vault ./biometric-signing.
And combined with the polkit policy, the polkit message also makes sense.
I have not looked at all of the packaging formats, but given that the node ipc proxy is also a separate process (in appimage this runs under a directory under /tmp f.e), I don't think this should be an issue?
@jivanpal
This would be simple enough for native packages like DEB and RPM, but I don't think it's feasible for bundled and sandboxed versions like AppImage and Snap.
I believe you meant Flatpak and Snap, since AppImage isn't really sandboxed. Regardless, even if it doesn't work with them (which I have not verified/tested), we could fall back to unlock with another non-biometric factor on startup in that case.
Also, I'm not sure if Flatpak and Snap will work with biometric authentication at all, since I would expect polkit to be sandboxed away, and I do not see an xdg portal or similar for it. 1Password does no offer biometrics (system authentication) on snap/flatpak f.e. On these systems I would just set biometrics available to false for now, and a separate PR could update that in the future.
Aside: Is there an official Flatpak version of Bitwarden? I see com.bitwarden.desktop on Flathub, but I don't see it mentioned on the website at https://bitwarden.com/download/.
This is a community provided wrapper. The very bottom of the FlatHub page says:
This wrapper is not verified by, affiliated with, or supported by 8bit Solutions LLC.
When I force-pushed the re-base of this PR for the reworked desktop biometrics, some automation in GitHub requested a review, but this is not ready for a review so I have converted the PR to a draft for now.
The PR is in a working state again, and even does set-up of the polkit policy now. I think it should be in a state that's good enough for review now, albeit it does not have a biometrics derived key, and thus should require the password/pin on restart.
Since it's been a while since I have worked on this request, and in the meantime a biometrics rework happened, I'll summarize the changes of this PR.
Currently, the PR implements biometric unlock via polkit. For this to work, a polkit policy has to be created in /usr/share/polkit-1/actions/. When first enabled in the settings, the desktop client elevates privileges via polkit (pkexec) to write the Bitwarden unlock policy to the directory.
For unlocking, the rust module is invoked, which uses the zbus_polkit crate to authenticate using the polkit policy.
Checking whether system authentication is available, is done by checking whether the polkit policy directory exists. This verifies that polkit is at least installed on the system, and not prevented from access by a sandbox (like when running in snap/flatpak).
This should be presence only, there is no biometrics-derived key implemented in this PR. Because of this, the default setting is "Require password or PIN on app start".
I mostly stuck to how the mac service was implemented, and did not look closely at the windows version.
Minor question for debate: I left the messages as "Unlock with Polkit" / "Polkit settings" etc. However, I don't think a regular user would know what Polkit is, so would "Unlock with System Authentication" be better for discoverability?
Those are all interesting options.
The Secret portal allows per application secrets to be retreived for sandboxed apps such as Flatpak (Snap?).
I like this approach, but, as you point out, it's not sufficient for the same reasons Windows UWP isn't a solution. The threat model is from non-sandboxed applications and since it provides no sandboxing from those applications, it really only provides security for systems which exclusively run Flatpak. I don't know of any that have invested that far.
store the secret in a file inaccessible to the user in the app directory
This is an interesting approach, but I'm unsure about delivery. I'm not familiar enough with our distribution channels to know if we could have this separate executable bundled across all of them. We could rework the download experience to be an installer-type thing...
Is it possible to enable this kind of functionality by executing sub-processes from the electron main thread as a part of the native messaging library? I'm thinking the effect would be something like running.
sudo -u bitwarden cat ./biometric-signing.crtPlease forgive my ignorance here, this is all pretty exploratory for me.
pass it to a daemonized bitwarden service.
I'd like to avoid a daemonized process if we can. We could enable other great features (like getting rid of session tokens in CLI) that I've thought though in the past, but requiring one is also a barrier to entry that would be good to avoid.
I think the guaranteed way forward will be to wire up a non-optional
require password on app startand eventually work in ways to expand our linux biometric feature offering as the above ideas develop.If you're more interested in fleshing out the above ideas, totally cool. My point is that I'd also entertain a PR dedicated to just enabling biometric after first unlock on Linux.
By the way, I thought about this a bit more. Instead of having a cert file on the filesystem (dangerous in case full disk encryption is not present), it would be better to leverage the TPM for this (if available). That way, even if the disk contents are leaked (stolen, leaked from a disk backup etc.), it would not aid in cracking the offline vault. But that is out of scope for this PR.
And on this point:
I think the guaranteed way forward will be to wire up a non-optional
require password on app startand eventually work in ways to expand our linux biometric feature offering as the above ideas develop.
Right now it is the default require password on app start, but it is possible to turn it off. Should I change the PR to not even give the option for not having to enter the password on app start? (I'm ok with either, though when giving the option the security implications should be made clear).
I have this on my todo list for the week. Thanks for the update!
Right now it is the default require password on app start, but it is possible to turn it off. Should I change the PR to not even give the option for not having to enter the password on app start? (I'm ok with either, though when giving the option the security implications should be made clear).
Yes, barring your TPM expansion, the split key model is the only way I can see to meet our security requirements.
that's a loong weekly schedule
I have this on my todo list for the week. Thanks for the update!
Right now it is the default require password on app start, but it is possible to turn it off. Should I change the PR to not even give the option for not having to enter the password on app start? (I'm ok with either, though when giving the option the security implications should be made clear).
Yes, barring your TPM expansion, the split key model is the only way I can see to meet our security requirements.
Forced the enter password on application start now by removing the UI settings on Linux.
Hey, just wanted to briefly say that this PR is greatly valued with continued interest.
It would be great to hear what bitwarden developers (eg. @MGibson1) think about it's status.
It's been nearly another month, are there any updates on this? @MGibson1
@quexten would probably need to update it
@quexten would probably need to update it
From my perspective it's missing a review from Bitwarden's side. There are no further code changes planned from my side at the moment.
After being reviewed, but before getting merged I will revisit the Polkit policy installation as I have noticed in another project that this requires SElinux related permissions change of the the Polkit file on some distributions.
Ok that seems appropriate, anyways thanks for your efforts đ