Ξ

The plan to support per-app passwords on the linux desktop

Published on 2024-07-27 code linux security

On the linux desktop we have the secret service dbus API to securely save passwords. This API is provided by services such as gnome-keyring, KWallet, or KeePassXC. However, the exact threat model is often confusing for users: unlinke similar APIs on iOS or Android, the secret service API has no provisions to isolate passwords that belong to app A from passwords that belong to app B.

In an attempt to fix this I created an experimental implementation called xi-keyring that used /proc/{pid}/exe to identify the calling app. I quickly had to realize that this doesn't work. For one, this value often ended up being python3.9 which both fails to effectively separate apps and will also break on python updates. But more importantly, I learned that it is possible to hijack binaries using LD_PRELOAD. So this was a dead end.

I recently visited a GNOME event and learned that a solution to this problem is in the works. On the one hand I was delighted. On the other hand, I was a bit disgruntled that they had not communicated their plans more clearly. I might have saved quite some time if I had known that sooner.

So in this post I will try to give an account of the plan as it was explained to me so that others don't have to travel to some conference to learn about it.

The app is the sandbox

As explained before, we need to get an app_id. My experiments so far have not yielded a reliable way to do this and a friend of mine who works in security said that it is actually impossible with the classic UNIX process model. However, nowadays we have sandboxing.

The idea is that we use a separate dbus socket that is tagged with an app_id for every sandbox. Flatpak currently does this by using xdg-dbus-proxy and storing the app_id in /.flatpak-info in its namespace. However, there are also plans to add this mechanism to dbus itself (the best explanation for this plan I could find is in a busd issue).

Of course, this only solves the problem for sandboxed apps. It seems like isolating unsandboxed apps is simply impossible. Still, there could be easier ways to sandbox applications. I could imagine a tool that only provides the tagged dbus socket without much other isolation.

A new API

With sandboxing we got a new set of dbus APIs called portals. They are provided by xdg-desktop-portal which does the app_id detection and then delegates to desktop specific backends.

There is a secret portal, but it is very different from the old secret service: Instead of allowing to store and retrieve passwords from the keyring, it only allows to retrieve a single "master secret".

When I saw this I was a bit confused and quickly decided that, whatever this was, this was surely not related to the secret service. In a way it is even the opposite: The secret service takes care of encrypting passwords at rest and keeping them out of swappable memory, but does not isolate apps from each other. The secret portal isolates apps from each other, but leaves all the other stuff to the apps.

The piece of the puzzle I was missing was libsecret, a library that has traditionally been used to interact with the secret service, at least in the GNOME/GTK/GLib world. Nowadays, when libsecret is being used inside of a sandbox, it will automatically switch to using the portal and provide the missing bits: It will use the master secret from the portal to store the passwords in an encrypted file. This "file backend" has been added as far back as 2018.

A new GUI for password management (?)

Now, I don't really care if a feature is implemented in a service or a library. There is really not that much of a difference. The much bigger change is that the details of password management are no longer part of the specification.

With the old secret service, I was able to manage, backup, sync, or lock my passwords because they were all in one place. With this new system, the passwords are scattered all over the place.

There is a new GUI called Key Rack that is aware of libsecret's file backed and can therefore provide centralized password management, but only for applications that use libsecret (or something that is compatible with it, we will get to that). Eventually, Key Rack might also gain the ability to backup or sync passwords. However, the portal does not allow to tell applications to forget about their master secret, so even Key Rack will never be able to support locking the keyring.

However, I am not completely sure if this is intentional or not. When the secret portal was first discussed, Stef Walter referenced a talk in which he claimed that users don't actually want a password manager, they want applications to be able to store passwords in a secure way (I am extrapolating a bit). Here is a quote from the confused user I linked at the very start of this post that seems to support that claim:

I was really surprised when I discovered Gnome Keyring, a tool that I never heard about and that was saving all my passwords in PLAINTEXT without my consent.

This discrepancy between Stef's talk and Key Rack only became clear to me after the event, so I was not able to ask people about it. But to me it seems like the original intent (to drop password management) was forgotten along the way. If we put password management back into the mix, libsecret's file backend doesn't make a lot of sense. The cleaner solution then would be to keep all passwords in the secret service and add access control based on app_id.

A new file format

The file format that libsecret's file backed uses to store passwords is different from the one that is being used by gnome-keyring. With gnome-keyring, only the passwords themselves are encrypted. With libsecret's file backend, the whole file is encrypted. This is generally a good thing.

There is a small caveat: Sometimes it can be confusing for users to be asked to unlock the keyring for an application that doesn't actually have any passwords stored. If the metadata is not encrypted, we can first make sure that the request is relevant before interrupting the users. When doing my own implementation, I was going back and forth between these options. But in the end I also decided on full encryption.

A new implementation

libsecret and especially gnome-keyring are both old and complex C codebases. So naturally, someone rewrote the whole thing in rust.

oo7 started out as a rust library that is more or less equivalent to libsecret. Crucially, it also implements a compatible file backend. But later on it also gained an implementation of the secret portal and there is an open pull request to also implement the secret service. So oo7 will likely replace gnome-keyring at some point.

Just as with libsecret, you can let oo7 pick a suitable backend automatically. But different from libsecret, you can also pick the backend explicitly. That makes it possible to migrate existing passwords from one backend to the other.

Conclusion

I am honestly not sure how far along this whole thing is in practice. oo7 is not quite ready yet, but seems to be the way forward. In theory, any application that is using libsecret should automatically switch to the file backend when sandboxed. Still, I could not find any traces of that on my personal system.

Even though it is good to know that there is a solid plan, there is a severe lack of documentation. Until I submitted a merge request, libsecret's documentation didn't even mention the file backend. There is also no clear governance process for the portals APIs. From the outside, it is very hard to understand why the specs ended up like they have.

And I believe this lack of information does not only affect developers like me. When users are confused as to why all their passwords are stored in gnome-keyring, that also points to a lack of information. We have two ways to react: We could, as Stef Walter was proposing in 2013, remove any features that users don't expect. That is valid approach.

But the opposite approach is equally valid: We could proudly present to users that the linux desktop comes with a built-in, secure, and trustworthy password manager. We could have a persistent status indicator that shows whether the keyring is currently unlocked. There could be notifications or prompts whenever an applications tries to access a password. I am not sure, I am probably going a bit too far here.

The point is: In other areas we are trying to push things forwards. Why not here?


Thanks to Maximiliano, Niels De Graef, Zeeshan Ali Khan, and Janis König for explaining all this stuff to me.