NSS compatibility for Open SSL apps

The purpose is library is to make converting an existing product that uses OpenSSL to use the NSS crypto library instead and to cause as few changes to the code you are trying to port as possible. Some changes are inevitable, particularly when crypto outside of SSL is being used, but for a general-purpose SSL client or server the goal is that 80% of the code can remain untouched.

In order to shake out the pieces of the API the following packages were converted using nss_compat_ossl:

stunnel

wget

libcurl

Each is lacking something, perhaps something important, but basic SSL support is done.

Currently Supports

Creating an SSL server listener and accepting requests

Creating an SSL client socket and making requests

Ciphers that should be compatible with OpenSSL

Client certificate authentication

Token password prompting/handling

Things to be done

We should import referenced certificates on the fly into our NSS database. A PKCS#11 module to do this has been started but requires NSS 3.12 so it is of limited use in the short-term.

Many missing pieces of the API

How To Use the Library

For the short term (see nss_compat_ossl#Reading PEM files) applications will need to use an NSS database. This consists of 3 files: cert8.db, key3.db and secmod.db located in the same directory. In order for the target to find the right database the environment variable SSL_DIR needs to be set to the location of your NSS database (unless an appropriate cert is installed in the default NSS database in /etc/pki/nssdb)

The code doesn't currently support file-based certificates. It uses the path of the certificate passed to SSL_CTX_use_certificate_file() and SSL_CTX_use_certificate_chain_file() as the nickname of the certificate in the NSS database. To list the certificates (and their nickname) in an NSS database you can use this:

% certutil -L -d /path/to/database

If you have a PKCS#12 file containing you can import it into your NSS database with:

The library currently lack nice, importable autoconf rules. Developers will need to tell their application where to find the NSPR and NSS include and libraries. pkg-config with the package names of nss and nspr can be used to determine this.

The variables HAVE_NSS and HAVE_OPENSSL can be used to differentiate between NSS and openSSL in that 20% of cases not handled by nss_compat_ossl.

The include file "nss_compat_ossl.h" must be included, be careful to not include any openSSL header files.

Some specific things to watch out for:

OpenSSL CRL handling is very different from NSS so any OpenSSL CRL handling code should be ifdef'd out. NSS handles CRLs directly. Users can use the crlutil tool to load them into the NSS database.

The callbacks for info_callback and verify_callback are made but often those functions use very diverse OpenSSL calls that aren't supported yet (and may never be). These callbacks will likely all need to be rewritten for NSS.

Few of the BIO_ calls are implemented. If these are used extensively in the target application then some major rewriting may be needed. Best to request some assistance before proceeding.

nss_compate_ossl doesn't use OpenSSL structures in most cases so any programs trying to access specific elements may need to change, possibly to use accessor functions.

NSS supports two modes for its SSL cache: threaded and multi-process. The nss_compat_ossl code currently initializes the cache for multi-threaded operation. If you need multi-process you will need to call these in your application:

No, as with using any FIPS validated module, all applications must comply with the security policy of that module document to claim FIPS conformance. The NSS security policy was written so that apps can easily comply with the requirements.

Most requirements are met simply by turning on FIPS mode in NSS. The FIPS module automatically prevents most illegal operations with respect to FIPS compliance. The security policy points out those areas where it does not.

Sample Application

When learning something new there is little better than a concrete example to ease things along.

I'll use stunnel 4.15 as a starting point. It is what I used to help develop nss_compat_ossl.

The first thing we have to do is tell autoconf about nss_compat_ossl and possibly override any default OpenSSL settings. This includes any include directories, libraries and any definitions (like HAVE_OPENSSL).

Here is a diff of the stunnel configure.ac. I'll go into the details of what and why after the fold.

The stunnel configure.ac does things a little differently than others I've seen. They declare a separate function to try to find OpenSSL then set things up from there. What we need to do is short circuit this. There is no AC_UNDEFINE so our stuff has to come first.

The first change in the file is in checkssldir(). This bails out if nss_compat_ossl has been selected. We check for a global variable. The interesting stuff comes next.

The meat is in the --with-nss_compat field. If it it gets set to a directory then we use that path (like --with-nss_compat=/usr/local) and we set CFLAGS and LIBS as best we can. If it is set without a DIR (e.g --with-nss_compat) then we use pkg-config to get things setup for us which is more likely to produce the results we want.

The important things to set are:

AC_DEFINE(HAVE_NSS_COMPAT)
LIBS to include the NSS and NSPR libraries and potentially the path they are installed in (if not in /usr/lib)
CFLAGS or INCLUDES to include the NSS and NSPR include file locations

The change of "if test -z "$ssldir -a -z "$nss_compat" we want to crap out if neither OpenSSL nor nss_compat_ossl are set.

Now that we can configure things properly you would run something like:

% autoconf
% ./configure --with-nss_compat

You should probably verify that the Makefiles define -DHAVE_NSS_COMPAT=1 and that LIBS and CFLAGS are sane.

Next we move onto the code changes. You can tackle it either as an iterative or analytical process. I tend to use the iterative approach myself. This involves trying to build and tackling errors as they come up.

In the stunnel case we run into a problem quite early. common.h is trying to include some OpenSSL headers which it can't find. We need it to use our header instead:

You could easily use the reverse of this and use #ifndef HAVE_NSS_COMPAT. The choice is yours.

Our next obstacle is in options.c. A slew of missing BIO_ errors are reported, way to many to include here. A further investigation reveals that the function generates a base64 value from a string passed in. In this case NSS provides a single function to do this so we can patch this with:

NSS handles CRLs via its database. OpenSSL imports them from flat files on startup. So we can #ifdef around a fairly large block of code as NSS will handle all this for us. It does mean though that the user that runs the program will need to preload the CRL into the NSS database using /usr/bin/crlutil before starting stunnel. So this should be documented somewhere.

This change is also very significant. In both OpenSSL and NSS you can write your own callback to verify certificates, CRLs, etc. nss_compat_ossl does a lot of this for the user so we can #ifdef around this code. It is possible to go ahead and define a verify_callback that does real work in both but the nss_compat_ossl API is missing a lot of functions typically found in this verification so you will have to proceed cautiously.

nss_compat_ossl automatically checks for certificate trust and valid dates. If there are other things you require you can continue to do them in a verify_callback() but it may be tricky.

NSS handles CRL checking automatically so we can ignore all of the following code:

And finally a very important change. nss_compat_ossl does not necessarily mimic the data structures of OpenSSL so any attempts to directly access parts of a structure will probably fail. In nss_compat_ossl there is only a logical difference between an SSL structure and a SSL context structure. So we can't pass in s->ctx. But we can pass in s to get what we want. This doesn't actually do much since we aren't calculating this data. I included this to demonstrate one possible workaround.

Now stunnel should build but it isn't quite ready to run yet. With NSS you have to tell it where it can find the key and certificate databases. We use the environment variable SSL_DIR to specify one (the default is /etc/pki/nssdb).

Congratulations. You've ported your first application with just a handful of changes to the code.