Using SASL: CMU's Cyrus SASL Library

The Simple Authentication and Security Layer (SASL)
allows administrators to configure just the right amount of security
for the networked applications in their environments.
A previous article explained SASL;
in this article,
we see how to program with SASL using an open source library.

1. Introduction

In a previous article,
we took a look at a "meta API" called
the Simple Authentication and Security Layer (SASL).
The idea behind SASL is that when you design an application protocol,
you can treat the communications security technologies as loadable
modules.

SASL shifts the burden of picking and choosing the technologies
used for authentication, integrity checking, and privacy from you,
the application writer,
to the network administrator.

For example,
depending on the environment,
some administrators may choose to configure authentication and
integrity-checking,
but no privacy.
As an application writer,
your job isn't to make decisions like this -- instead,
your job is to provide the network administrator with maximal
flexibility to pick the appropriate
level of security for the job at hand.

Once you decide to use SASL,
your next choice is whether to roll your own SASL implementation or to
use an existing library.
Speaking as someone who's done both,
I'll tell you that while it may be necessary -- in very constrained environments -- to write your own SASL layer,
it's almost always better to use an off-the-shelf library.

In this article,
we'll look at Carnegie Mellon University's (CMU) Cyrus SASL.
This is the SASL library used by sendmail, OpenLDAP, and the
Cyrus ACAP and IMAP servers.
The native library is written in the C programming language,
and it also comes with a Java interface.
(In addition,
for you Tcl aficionados,
you can find a Tcl extension to Cyrus SASL in the
beepcore.tcl project
on SourceForge.)

For the rest of this article,
we'll be talking about Cyrus SASLv2.
Also,
just to set expectations,
this article doesn't give detailed API information -- it's an overview, not a reference manual, for Cyrus SASL.

All of the Cyrus SASL releases can be found
here,
so start by looking for a distribution named cyrus-sasl-2.*.tar.gz.
On my production systems,
I use Cyrus SASL v2.1.2,
but on my development system,
I use anonymous CVS to stay in sync with the Cyrus developers.

Once you've extracted the tar image,
do the two usual things:
look at the README file and then run configure --help to
determine which configuration options are available.
You might also want to browse doc/install.html for some tips on
where to find the libraries that Cyrus SASL likes.

After configure successfully runs,
it's just a make, su, make install,
and we're ready to go.

2. The Ground Rules

The procedure calls that "do SASL" are pretty simple,
even if the work they do is somewhat complex.
However,
before you actually "do SASL,"
you have to setup some data structures so that Cyrus SASL will
interface correctly to your program and your environment.
There are two of these "universal" data structures.

The first is the callback data structure.
When Cyrus SASL is in the middle of doing something and it needs more
information,
it consults a callback.
Each callback has three fields:

an identifier,
which is a C-preprocessor symbol (e.g., SASL_CB_PASS);

a pointer to a procedure; and

a pointer to a void * that's passed to the procedure.

The parameter list for the procedure depends on the value of the
identifier --
each callback has a different parameter list.
(Because of the way C handles indirect procedure calls,
you must be very careful to make sure that the procedure you define
provides the exact parameter list expected by Cyrus SASL --
if not,
there will be trouble,
and there's nothing that Cyrus SASL can do about it.)

Cyrus SASL has a lot of callbacks,
and although some are "generic," most are either server-only or client-only.
Fortunately,
you can pretty much get by with defining only two:

SASL_CB_LOG,
which is called to write an entry to your program's logfile;
and,

SASL_CB_GETOPT,
which is called to get configuration information.

For example,
if your program has its own config file,
the procedure you point to in the callback should look at your
program's config file for SASL-specific information.

Cyrus SASL gives you two opportunities to define your callback list.
The first is when you initialize the library,
and the second is whenever you establish a network connection.
This leads us to the second data structure,
called a context, which keeps track of the SASL state associated with a network connection,
from cradle to grave.

3. Client-side SASL

The first step is to call the routine that initializes Cyrus SASL
and pass it your global callback list.
Cyrus SASL actually provides two initialization routines,
one for client-side and the other for server-side behavior.
Based on how your program is going to behave,
feel free to call just one or both.

The second step is to make an outgoing connection and then create a
client context for it.
Now at this point your program has to do a bit of thinking about whether it should
negotiate the use of TLS.
(After you do SASL,
it's too late to start TLS.)
How you make this choice is up to you,
but here are some things to consider:

If there's a configuration option or a command-line argument saying
that you must (or not to bother),
then the decision is easy.

If the operating system tells your program the connection is
already encrypted (e.g., you're running over a VPN or using IPSEC),
then there's no point in doing TLS.

If the server didn't advertise TLS,
then the decision is easy.

If the server advertised a SASL mechanism that can support security
(e.g., DIGEST-MD5 or KERBEROS_V4),
then you can tell Cyrus SASL to require privacy instead of doing TLS.

Regardless,
at this point you tell Cyrus SASL the actual and desired
security level of the connection,
expressed as an integer.
Typical values are 0 (nothing), 1 (integrity-checking only)
56, 112, and 128.
Excluding zero and one,
it's easiest to think of this as the number of bits in the
session key being used for privacy.

Now comes the fun part -- you call a routine that takes the SASL mechanisms advertised by the
server, and it tells you:

which SASL mechanism to tell the server to start; and

the "initial" data to pass to the server, if any.

(Or,
if your requirements aren't compatible with what the server offers,
you'll get back an error return instead.)
The key thing to understand here is the division of labor:
your program is responsible for moving the bits back and forth,
and Cyrus SASL is responsible for figuring out what bits are sent and
interpreting whatever bits are sent back.

Although this sounds straightforward,
there's a little bit of ugliness we have to digress into.
Some protocols don't let the client supply initial data to the server
when they ask to start a SASL mechanism.
For example,
SMTP allows initial data,
but IMAP doesn't.
When you call the routine to start the client negotiation,
you tell Cyrus SASL whether initial data is allowed.

Let's look at an example.
Say you're writing an SMTP client and you want to do SASL stuff.
When you connect to an SMTP server:

The server ("S:") will identify itself with a 220 greeting,
your program,
the client ("C:"),
sends an EHLO command to find out what services
are offered,
and the server says it supports SASL with four mechanisms (AUTH ...),
TLS (STARTTLS),
and the help command.

Now your program has to decide whether to do TLS,
since the server advertised it.
If so,
your program tells the server to start TLS,
and ultimately your program gets back another 220 greeting,
and sends another EHLO.
(Obviously, it's a bit more complicated than this -- check out RFC 3207
for all the details.)

Regardless of whether you're doing TLS,
your program creates a client context for Cyrus SASL,
and sets the actual and desired security properties for that context.
Next,
your program strips the 250-AUTH from the server's response and
tells Cyrus SASL that you're ready to do SASL.

The routine that decides what your program should tell the server
is pretty complicated.
First,
it has to determine which available SASL mechanisms
(termed "plug-ins") meet your security requirements
and are supported by the server.
Then it tries to initialize them in order of preference.
During this process,
the plug-in may need additional information from your program
(e.g., a username or a password).
This is where one or more client-side callbacks are invoked.
This is also where you'll find the most complexity in the Cyrus SASL
client-side interface.