Network Security Services (NSS)

Network Security Services, or NSS, is a set of libraries that was originally developed by Netscape and later inherited by Mozilla. In Ubuntu, it’s used mainly in Mozilla products such as Firefox and Thunderbird, but there are modules and language bindings available for other packages to use.

Given its origins in the Netscape browser, this library used to be bundled together with the applications that required it. Up to this day, for example, the Debian package of Mozilla Thunderbird has its own copy of libnss3, ignoring the system-wide one shipped by the libnss3 Debian package.

Config file

NSS doesn’t have a system-wide policy configuration file in Ubuntu (see #2016303 for details). That leaves the remaining location for the configuration file to be in the NSS “database” directory. Depending on the application, it can be in the following places by default:

  • ~/.pki/nssdb/pkcs11.txt This is where the system-provided libnss3 library will look by default.

  • ~/snap/firefox/common/.mozilla/firefox/<random>.default/pkcs11.txt This is where the Firefox snap will look.

  • ~/.thunderbird/<random>.default-release/pkcs11.txt Mozilla Thunderbird ships with its own copy of libnss3, and is configured to look into this directory to find it.

  • ~/.netscape/pkcs11.txt This is the default used by the NSS tools shipped in the libnss3-tools Debian package.

The directory where pkcs11.txt is looked up is also the NSS database directory. NSS will store the certificates and private keys it imports or generates here, and the directory will typically contain these SQLITE3 database files:

  • cert9.db: certificates database

  • key4.db: private key database

With the pkcs11.txt file we can load PKCS#11 modules, including the one built into NSS itself. Other examples of modules that can be loaded from there are modules for smart cards or other hardware-based cryptographic devices. Of interest to us here, though, is the policy module.

Configuring the NSS policy module

The policy module is defined like this in pkcs11.txt:

library=
name=Policy
NSS=flags=policyOnly,moduleDB
config="disallow=<list> allow=<list> flags=<flags>"

It’s via the config= line that we can list which cryptographic algorithms we want to allow and disallow. The terms in the list are separated with a colon (”:”) and consist of the following:

  • The special keyword “ALL”, meaning all possible values and algorithms. It’s mostly used with disallow, so that a clean slate can be constructed with a following allow list. For example, disallow=ALL allow=<list of allowed> would only allow the algorithms explicitly listed in the allow list.

  • Algorithm name: Standard names like sha256, hmac-sha256, chacha20-poly1305, aes128-gcm and others.

  • Version specifiers: A minimum or maximum version for a protocol. These are the available ones:

    • tls-version-min, tls-version-max: Minimum and maximum version for the TLS protocol. For example, tls-version-min=tls1.2.

    • dtls-version-min, dtls-version-max: As above, but for DTLS (TLS over UDP)

  • Key sizes: Minimum size for a key:

    • DH-MIN: Diffie-Helman minimum key size. For example, DH-MIN=2048 specifies a minimum of 2048 bits.

    • DSA-MIN: Digital Signature Algorithm minimum key size. For example, DSA-MIN=2048 specifies a minimum of 2048 bits.

    • RSA-MIN: RSA minimum key size. For example, RSA-MIN=2048 specifies a minimum of 2048 bits.

  • Signature qualifier: Selects the specified algorithm with a specific type of signature. For example, sha256/cert-signature. Here are some of the qualifiers that are available:

    • /cert-signature: Used in certificate signatures, certificate revocation lists (CRLs) and Online Certificate Status Protocol (OCSP).

    • /signature: Used in any signature.

    • /all: Combines SSL, SSL key exchange, and signatures.

    • /ssl-key-exchange: Used in the SSL key exchange.

    • /ssl: Used in the SSL record protocol.

The disallow rules are always parsed first, and then the allow ones, independent of the order in which they appear.

There are extra flags that can be added to the config line as well, in a comma-separated list if more than one is specified:

  • policy-lock: Turn off the ability for applications to change policy with API calls.

  • ssl-lock: Turn off the ability to change the SSL defaults.

Practical examples

Let’s see some practical examples of how we can use the configuration file to tweak the default cryptographic settings of an application linked with the system NSS libraries.

For these examples, we will be using the configuration file located in ~/.pki/nssdb/pkcs11.txt. As noted before, depending on the application this file can be in another directory.

The examples will use the tstclnt test application that is part of the libnss3-tools Debian package. For the server part, we will be using the OpenSSL test server on the same system. Since it uses the OpenSSL library, it won’t be affected by the changes we make to the NSS configuration.

Bootstrapping the NSS database

Install the libnss3-tools package which has the necessary tools we will need:

sudo apt install libnss3-tools

If you don’t have a ~/.pki/nssdb directory yet, it will have to be created first. For that, we will use the certutil command, also part of the libnss3-tools package. This will bootstrap the NSS database in that directory, and also create the initial pkcs11.txt file we will tweak in the subsequent examples:

mkdir -p ~/.pki/nssdb
certutil -d ~/.pki/nssdb -N

If you already have a populated ~/.pki/nssdb directory, there is no need to run the above commands.

When running the certutil command as shown, you will be asked to choose a password. That password will protect the NSS database, and will be requested whenever certain changes are made to it.

In the following examples we will make changes to the pkcs11.txt file inside the NSS database directory. The bootstrap process above will have created this file for us already. The changes that we will make should be added to the file, and not replace it. For example, these are the contents of ~/.pki/nssdb/pkcs11.txt right after the bootstrap process:

library=
name=NSS Internal PKCS #11 Module
parameters=configdir='/home/ubuntu/.pki/nssdb' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[ECC,RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})

When an example asks to configure the policy module, its block should be appended to the existing configuration block in the file. For example:

library=
name=NSS Internal PKCS #11 Module
parameters=configdir='/home/ubuntu/.pki/nssdb' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[ECC,RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})

library=
name=Policy
NSS=flags=policyOnly,moduleDB
config="allow=tls-version-min=tls1.3"

Test setup

For these examples, we will be using a simple OpenSSL server on the same system as the NSS client we are testing. For that we will have to generate a certificate and key for the OpenSSL server to use, and then import that into the NSS database so it can be trusted.

First, generate a keypair for OpenSSL:

openssl req -new -x509 -days 30 -nodes -subj "/CN=localhost" -out localhost.pem -keyout localhost.key

To avoid telling tstclnt to ignore certification validation errors, which might mask the crypto policy changes we are trying to demonstrate, it’s best to import this certificate into the NSS database and mark it as trusted:

certutil -d ~/.pki/nssdb -A -a -i localhost.pem -t TCP -n localhost

This command will ask you for the NSS database password that you supplied when bootstrapping it. The command line options that were used have the following meanings:

  • -d ~/.pki/nssdb: The path to the NSS database.

  • -A: Import a certificate.

  • -a: The certificate is in ASCII mode (PEM).

  • -i localhost.pem: The file to read (the actual certificate).

  • -t TCP: Trust flags (see the -t trustargs argument in the certutil manpage for a full list).

    • T: Trusted CA for client authentication.

    • C: Trusted CA.

    • P: Trusted peer.

  • -n localhost: A nickname for this certificate, like a label. It can be used later on to select this certificate.

We are now ready to begin our tests. Unless otherwise noted, this is how it’s expected that the server will be run:

openssl s_server -accept 4443 -cert localhost.pem -key localhost.key -www

The tstclnt tool

The libnss3-tools package also contains the tstclnt tool, which is what we will use in the following examples to test our NSS configuration changes.

This is the typical command we will use:

tstclnt -d ~/.pki/nssdb -h localhost -p 4443

Where the options have the following meanings:

  • -d ~/.pki/nssdb: Use the NSS database located in the ~/.pki/nssdb directory.

  • -h localhost: The server to connect to.

  • -p 4443: The TCP port to connect to.

To make things a bit easier to see, since this tool prints a lot of information about the connection, we will wrap it like this:

echo "GET / HTTP/1.0" | tstclnt -d ~/.pki/nssdb -h localhost -p 4443 2>&1 | grep ^New

New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
^C

The above tells us that the connection was completed and that it is using TLSv1.3, with a TLS_AES_128_GCM_SHA256 cipher suite.

It will not exit on its own, so it’s necessary to press Ctrl+C (^C) to get back to the shell prompt.

Only use TLSv1.3

Here is how we can restrict the TLS protocol version to 1.3 at a minimum:

library=
name=Policy
NSS=flags=policyOnly,moduleDB
config="allow=tls-version-min=tls1.3"

If we then start the OpenSSL server without TLSv1.3 support, like this (note the extra no_tls1_3 at the end):

openssl s_server -accept 4443 -cert localhost.pem -key localhost.key -www -no_tls1_3

The tstclnt tool will fail to connect:

echo "GET / HTTP/1.0" | tstclnt -d ~/.pki/nssdb -h localhost -p 4443 2>&1 | grep ^New
echo $?

1

To see the actual error, we can remove the grep at the end:

echo "GET / HTTP/1.0" | tstclnt -d ~/.pki/nssdb -h localhost -p 4443 2>&1

tstclnt: write to SSL socket failed: SSL_ERROR_PROTOCOL_VERSION_ALERT: Peer reports incompatible or unsupported protocol version.

If we allow the server to offer TLSv1.3:

openssl s_server -accept 4443 -cert localhost.pem -key localhost.key -www

Then the connection completes:

echo "GET / HTTP/1.0" | tstclnt -d ~/.pki/nssdb -h localhost -p 4443 2>&1 | grep ^New

New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
^C

Use only AES256 with TLSv1.3

In the previous example, the connection ended up using TLSv1.3 as expected, but AES128. To enforce AES256, we can disallow the 128-bit version:

library=
name=Policy
NSS=flags=policyOnly,moduleDB
config="disallow=aes128-gcm allow=tls-version-min=tls1.3"

This time the client selects something else:

echo "GET / HTTP/1.0" | tstclnt -d ~/.pki/nssdb -h localhost -p 4443  2>&1 | grep ^New

New, TLSv1.3, Cipher is TLS_CHACHA20_POLY1305_SHA256

We can remove that one from the list as well:

config="disallow=aes128-gcm:chacha20-poly1305 allow=tls-version-min=tls1.3"

And now we get AES256:

echo "GET / HTTP/1.0" | tstclnt -d ~/.pki/nssdb -h localhost -p 4443  2>&1 | grep ^New

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

References

Unfortunately most of the upstream Mozilla documentation is either outdated or deprecated, and the best reference available about the policy module at the moment is in the source code and tests.