Hasl?
Tags: programming, security, authentication, opensource
Published: 2023-07-13 14:00:00

Those of you that have been around Open Source/Specification protocols for a while may know of this thing called [SASL](https://www.rfc-editor.org/rfc/rfc2222.html) or Simple Authentication and Security Layer.

To summarize it succinctly, think of SASL like the libpurple of authentication. What I mean by that is that it abstracts all sorts of different authentication mechanism so you the application developer don't need to do so or care which authentication mechanism is actually used.

You just tell the library the authentication parameters you have and it'll negotiate with the other end doing all the hard work for you. As the application developer all you have to do is take the input from the other end of the connection, pass it to the SASL library. The SASL library will then give you some results for you to pass them to the other end of the connection. 

Most protocols have a way to start a SASL negotiation and a way to tell the application developer the results. We'll come back to this shortly.

While working on the new [IRCv3](https://ircv3.net/) protocol plugin for Pidgin 3 one of the first things we implemented was SASL support. However, the existing SASL libraries leave a bit to be desired. I don't want to spend too much time on this as it's mostly implementation details but there are a few things of note.

Both [Cyrus-SASL](https://www.cyrusimap.org/sasl/) and [GNU SASL](https://www.gnu.org/software/gsasl/) use callbacks for determining inputs like usernames, passwords and so on. This isn't a huge deal but it's definitely a bit clunky especially if you're trying to read passwords from a password manager and it locks on you between attempts. You can of course store these yourself to avoid this, but you're just working around the library at that point.

The bigger issue though is that none of the existing libraries keep track of which mechanisms have been attempted which leaves that task to the application developer. In the Pidgin code base we had a bunch of funky code to manage this in both IRC and XMPP. Obviously it would be preferably if this duplicated code didn't exist, but it was necessary due to differences in the way SASL was implemented between the protocols.

This put us in a bit of a tricky spot. The existing solutions worked, but were clunky, and left us with a fair amount of work due to keep track of which mechanisms were already attempted. To make matters worse, we recently found out that Cyrus-SASL in particular isn't exactly [GPLv2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) compatible even though we have supported using it for a very long time.

After a lot of consideration, I finally made the executive decision to, you guessed it, create a new implementation. That new implementation is named Hasl, for Hassle-free Authentication and Security Layer. The name is also a pun, because for me personally, working with Cyrus-SASL and GNU SASL were quite the hassle...

Unlike the alternatives, Hasl is only a client library. We have no plans right now to implement the server side piece of any of our supported mechanisms.

At the lowest level Hasl works just like the alternatives. You create a context/client, determine a mechanism to attempt, and then step through it until the mechanism either passes or fails.

Where Hasl really differs is at the high level where the application developer is concerned. As I mentioned earlier, the existing SASL implementations used callbacks to get values like username, password, etc. In Hasl you create a `context` that holds on to all of these values for the duration of the authentication process. When the authentication process is done, that memory is free'd and that's that. Hasl also has a `next` method that will tell Hasl to move to the next mechanism is the list meaning the application developer doesn't have to do anything more than that.

To try and put this in perspective, lets look at how SASL works at a protocol layer. To do that we're going to describe how it's handled in IRC as that's pretty straight forward.

For starters, an IRC connection has two stages, `registration` and `post registration`. Registration happens right after the connection is established and it is were stuff like authentication and capability negotiation occurs. Post registration is everything after that where you can join channels, send messages, and so on.

To start SASL authentication on IRC, you send the `AUTHENTICATE` command during registration with the name of the SASL mechanism you would like to attempt. There are many different [SASL mechanisms](https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xml#sasl-mechanisms-1), but we're just going to talk about the `PLAIN` mechanism here as it's quite simple.

The `PLAIN` mechanism is a client-first mechanism which means the client is the first one to send data. This is a little counter-intuitive at first as the server always has to send something first, but it's just ignored. In the case of 
IRC, the server sends a `+` to tell the client to continue. The basic exchange for the `PLAIN` mechanism looks like the following:

```
Client: AUTHENTICATE PLAIN
Server: AUTHENTICATE +
Client: AUTHENTICATE Zm9vAGJhcg==
Server: :server1 900 foo foo!bar@irc.example.com :You are now logged in as foo
Server: :server1 903 foo :SASL authentication successful
```

The value `Zm9vAGJhcg==` is the result of Base64 encoding the concatenation of `username` and `password` with `null` bytes between them. In other words it's `base64(username + "\0" + password)`.

The server responds with the `NUMERIC`s `900` and `903` which tells us that the authentication attempt was successful. At this point you're done with SASL for the rest of this connection's lifetime.

Next I wanted to describe the differences in implementations between Cyrus-SASL and Hasl but this post is already getting super long so I'm going to leave that as an exercise for you readers.

You can find the IRC implementation using Cyrus-SASL in purple2 [here](https://keep.imfreedom.org/pidgin/pidgin/file/release-2.x.y/libpurple/protocols/irc/msgs.c#l1489). This code is a bit all over the place, but you can find the `903 NUMERIC` handler [here](https://keep.imfreedom.org/pidgin/pidgin/file/release-2.x.y/libpurple/protocols/irc/msgs.c#l1732) which will cover the example described above. Finally, the code determining which mechanism to try next can be found [here](https://keep.imfreedom.org/pidgin/pidgin/file/release-2.x.y/libpurple/protocols/irc/msgs.c#l1747).

The IRCv3 SASL implementation using Hasl can be found [here](https://keep.imfreedom.org/pidgin/pidgin/file/default/libpurple/protocols/ircv3/purpleircv3sasl.c#l120). The `903 NUMERIC` handler can be found [here](https://keep.imfreedom.org/pidgin/pidgin/file/default/libpurple/protocols/ircv3/purpleircv3sasl.c#l283) and the code determining which mechanism to try next can be found [here](https://keep.imfreedom.org/pidgin/pidgin/file/default/libpurple/protocols/ircv3/purpleircv3sasl.c#l90).

As you can tell from looking at both implementations it's very difficult to quantify which one is easier to use. I'm of course partial to Hasl as I wrote it and it follows a GLib/GObject approach when it comes to the API.

Regardless, we recently pushed out version 0.1.0 of Hasl. I have opened an [ITP (Intent To Package) bug](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039879) to get the library into [Debian](https://debian.org) but that hasn't been accepted yet.

As I mentioned earlier, there is a licensing conflict here that I wasn't aware of, and it is currently threatening to get [Pidgin removed from Debian](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1036113). As such, since we already had Hasl working in Pidgin 3, it made sense to get the library to a state where we could port Pidgin 2 to it if necessary to limit any disruption to our users.

So far nothing has come of any of this yet, but we might be releasing a new version of Pidgin 2 with Cyrus-SASL replaced by Hasl in the near future if necessary.

I even went through and started to add a [bunch of features](https://issues.imfreedom.org/issues/HASL) to Hasl to help with this transition as we're going to need them sooner or later anyways. These changes will become Hasl 0.2.0 once everything is reviewed and merged.

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!