DEV Community

Gary Kramlich
Gary Kramlich

Posted on

Passwords, passwords, passwords!!

This article was originally posted on Patreon and has been brought over here to get all of the Pidgin Development/History posts into one single place.

We have quite the history when it comes to password storage... For many years we suggested either not storing passwords or or protecting access to the file containing those passwords. You can find complete reasoning to that suggestion here.

Fast forward a few years and the general consensus changed and storing a password behind a password was looked on as favorable, especially if you're using a strong password to protect the others. There's of course more to this, but we're going to gloss over that in this post.

As such, we had a Google Summer of Code project in 2008 to add Master Password Support. This initial work added support for an Internal Keyring, GNOME Keyring, and KWallet. It was merged to the pidgin3 branch in January of 2009 and there it sat, stuck waiting for Pidgin 3 which as you all know, still isn't released in 2022. But this didn't mean the API wasn't being maintained, it just couldn't be used by users.

In August of 2012, support for libsecret was added and eventually moved to its asynchronous API in the Fall of 2016.

In April of 2013, support was added for wincred which is a built-in password manager for Windows that's tied to your local login.

In May of 2018 GNOME Keyring was dropped as it had been supporting libsecret for quite some time.

Fast forward a bit more to October 2020. We knew for awhile that the keyring API had some issues. It wasn't GObject based, which means you wouldn't be able to add support for another provider in anything but C/C++ which is something we're very actively trying to avoid.

Then there was this weird migration API that never really made sense to me. Basically, when you switched keyrings in Pidgin, it would take all of the passwords you have stored, copy them to the new keyring, and then delete them from the original one. This was determined to be unexpected and unwanted behavior for end users.

It was at this point we started designing a new API. This API would be comprised of two parts, the CredentialManager and CredentialProviders.

The CredentialManager does kind of what you'd expect after last week's post. It keeps track of all of the CredentialProvider's as well as which one is currently active, but it adds a new layer on top as well. Rather than have everyone have to get the active provider from the CredentialManager and then use the CredentialProvider API on that provider, the CredentialManager provides a proxy to all of the CredentialProvider API and will do that work for you. While it's not a huge difference code wise as you can see below, it's still easier to deal with.

/* This example manually gets the active provider from the
 * manager and reads a password from it.
 */
PurpleCredentialManager *manager = NULL;
PurpleCredentialProvider *active = NULL;

manager = purple_credential_manager_get_default();
active = purple_credential_manager_get_active(manager);

purple_credential_provider_read_password_async(active,
                                               account,
                                               callback,
                                               NULL);
Enter fullscreen mode Exit fullscreen mode
/* This example uses the proxy API built into the manager to
 * save some code.
 */
PurpleCredentialManager *manager = NULL;

manager = purple_credential_manager_get_default();
purple_credential_manager_read_password_async(manager,
                                              account,
                                              callback,
                                              NULL);
Enter fullscreen mode Exit fullscreen mode

As you can see, the differences are minor, but much easier to deal with. You might also notice that we're using an asynchronous API here. The Credential API only supports asynchronous methods. If there's a need in the future we may add synchronous methods.

One of the other things we're pushing with all this new API and everything is that we're building things in a way that we can actually unit test them. So the Credential API has nearly 100% test coverage which is not something we've done very well in the past.

With the API designed, written, and tested, we started porting the existing keyrings to the new API. We started with libsecret as its API was very similar to what we ended up designing. Next we did KWallet, which was an experience as I've never written anything with QT before. After that we went ahead and ported wincred as well. All that left was the internal keyring, which could store passwords in accounts.xml encrypted or in plain text.

The internal keyring sat unported for a long time. This was mostly due to the fact that we didn't exactly want to build out the dynamic UI for it as it was based on PurpleRequestFields. Eventually we came to the realization that, while we may think we know what we're doing with cryptography, that doesn't mean that we're actually capable of securing our users passwords. So we took a page from our previous stance on password management and decided, we're just not going to provide our own CredentialProvider and decided that user can choose to either not store passwords or use an established credential provider. We'll still provide the glue code to talk to the big providers, but we're not going to try and implement our own.

Eventually we would go on and implement Keychain Access on macOS as well which means that Pidgin 3 can and will support the native password managers on all of the major platforms. But of course, we designed this new API to be pluggable, which all of these providers are implemented in plugins. But that means that anyone can write additional CredentialProviders for whatever service they would like! Of course this means its up to the user to choose which to use and below you can see a screen shot of the current preferences dialog Pidgin 3.

Image description

And that's how we're handling password storage in Pidgin 3. When an account tries to connect, it'll ask the CredentialProvider for the password and if it doesn't exist, you'll be prompted for it. If you check the "remember password" checkbox on that dialog, the password will then be stored in the CredentialProvider and you'll never need to type it again and it will actually be stored securely!

I hope you're enjoying these posts! Remember they go live for patrons at 9AM CST on Mondays and go public at 12AM CST on Thursdays! If there's something specific you'd like to see me cover here, please comment below!

Top comments (0)