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-providedlibnss3
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 oflibnss3
, 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 thelibnss3-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 databasekey4.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 followingallow
list. For example,disallow=ALL allow=<list of allowed>
would only allow the algorithms explicitly listed in theallow
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.