From 97da1c020bfd04448e065d87a12c876b57538638 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Mon, 8 Feb 2021 23:52:42 +0100
Subject: [PATCH v26 06/10] nss: Documentation

Basic documentation of the new API (keypass hooks) as well as config
parameters and installation. Additionally there is a section on how
to create certificate and keys using the NSS toolchain.
---
 doc/src/sgml/acronyms.sgml     |  43 ++++++++++++
 doc/src/sgml/config.sgml       |  28 +++++++-
 doc/src/sgml/installation.sgml |  31 ++++++---
 doc/src/sgml/libpq.sgml        |  97 +++++++++++++++++++++++++-
 doc/src/sgml/runtime.sgml      | 123 +++++++++++++++++++++++++++++++--
 5 files changed, 303 insertions(+), 19 deletions(-)

diff --git a/doc/src/sgml/acronyms.sgml b/doc/src/sgml/acronyms.sgml
index 4e5ec983c0..7b02c8a622 100644
--- a/doc/src/sgml/acronyms.sgml
+++ b/doc/src/sgml/acronyms.sgml
@@ -441,6 +441,28 @@
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><acronym>NSPR</acronym></term>
+    <listitem>
+     <para>
+      <ulink
+      url="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSPR">
+      Netscape Portable Runtime</ulink>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><acronym>NSS</acronym></term>
+    <listitem>
+     <para>
+      <ulink
+      url="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS">
+      Network Security Services</ulink>
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><acronym>ODBC</acronym></term>
     <listitem>
@@ -539,6 +561,17 @@
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><acronym>PKCS#12</acronym></term>
+    <listitem>
+     <para>
+      <ulink
+      url="https://en.wikipedia.org/wiki/PKCS_12">
+      Public-Key Cryptography Standards #12</ulink>
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><acronym>PL</acronym></term>
     <listitem>
@@ -684,6 +717,16 @@
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><acronym>TLS</acronym></term>
+    <listitem>
+     <para>
+      <ulink url="https://en.wikipedia.org/wiki/Transport_Layer_Security">
+      Transport Layer Security</ulink>
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><acronym>TOAST</acronym></term>
     <listitem>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5ef1c7ad3c..5482d83e65 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1238,6 +1238,23 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-ssl-database" xreflabel="ssl_database">
+      <term><varname>ssl_database</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>ssl_database</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the name of the file containing the server certificates and
+        keys when using <productname>NSS</productname> for <acronym>SSL</acronym>
+        connections. This parameter can only be set in the
+        <filename>postgresql.conf</filename> file or on the server command
+        line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
       <term><varname>ssl_ciphers</varname> (<type>string</type>)
       <indexterm>
@@ -1254,7 +1271,9 @@ include_dir 'conf.d'
         connections using TLS version 1.2 and lower are affected.  There is
         currently no setting that controls the cipher choices used by TLS
         version 1.3 connections.  The default value is
-        <literal>HIGH:MEDIUM:+3DES:!aNULL</literal>.  The default is usually a
+        <literal>HIGH:MEDIUM:+3DES:!aNULL</literal> for servers which have
+        been built with <productname>OpenSSL</productname> as the
+        <acronym>SSL</acronym> library.  The default is usually a
         reasonable choice unless you have specific security requirements.
        </para>
 
@@ -1456,8 +1475,11 @@ include_dir 'conf.d'
        <para>
         Sets an external command to be invoked when a passphrase for
         decrypting an SSL file such as a private key needs to be obtained.  By
-        default, this parameter is empty, which means the built-in prompting
-        mechanism is used.
+        default, this parameter is empty. When the server is using
+        <productname>OpenSSL</productname>, this means the built-in prompting
+        mechanism is used. When using <productname>NSS</productname>, there is
+        no default prompting so a blank callback will be used returning an
+        empty password.
        </para>
        <para>
         The command must print the passphrase to the standard output and exit
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 66ad4ba938..1b71881c6e 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -976,15 +976,30 @@ build-postgresql:
        <listitem>
         <para>
          Build with support for <acronym>SSL</acronym> (encrypted)
-         connections. The only <replaceable>LIBRARY</replaceable>
-         supported is <option>openssl</option>. This requires the
-         <productname>OpenSSL</productname> package to be installed.
-         <filename>configure</filename> will check for the required
-         header files and libraries to make sure that your
-         <productname>OpenSSL</productname> installation is sufficient
-         before proceeding. 
+         connections. <replaceable>LIBRARY</replaceable> must be one of:
         </para>
-       </listitem>
+        <itemizedlist>
+         <listitem>
+          <para>
+           <option>openssl</option> to build with <productname>OpenSSL</productname> support.
+            This requires the <productname>OpenSSL</productname>
+            package to be installed.  <filename>configure</filename> will check
+            for the required header files and libraries to make sure that
+            your <productname>OpenSSL</productname> installation is sufficient
+            before proceeding.
+          </para>
+         </listitem>
+         <listitem>
+          <para>
+           <option>nss</option> to build with <productname>libnss</productname> support.
+            This requires the <productname>NSS</productname> package to be installed. Additionally,
+            <productname>NSS</productname> requires <productname>NSPR</productname>
+            to be installed. <filename>configure</filename> will check for the
+            required header files and libraries to make sure that your
+            <productname>NSS</productname> installation is sufficient before
+            proceeding.
+          </para>
+         </listitem>
       </varlistentry>
 
       <varlistentry>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b7a82453f0..ad2ec93848 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -830,6 +830,12 @@ int callback_fn(char *buf, int size, PGconn *conn);
        <function>longjmp(...)</function>, etc. It must return normally.
       </para>
 
+      <para>
+        <function>PQsetSSLKeyPassHook_OpenSSL</function> has no effect unless
+        the server was compiled with <productname>OpenSSL</productname>
+        support.
+      </para>
+
      </listitem>
     </varlistentry>
 
@@ -846,6 +852,70 @@ PQsslKeyPassHook_OpenSSL_type PQgetSSLKeyPassHook_OpenSSL(void);
 </synopsis>
       </para>
 
+      <para>
+        <function>PQgetSSLKeyPassHook_OpenSSL</function> has no effect unless
+        the server was compiled with <productname>OpenSSL</productname>
+        support.
+      </para>
+
+     </listitem>
+    </varlistentry>
+
+    <varlistentry id="libpq-pqsetsslkeypasshook-nss">
+     <term><function>PQsetSSLKeyPassHook_nss</function><indexterm><primary>PQsetSSLKeyPassHook_nss</primary></indexterm></term>
+     <listitem>
+      <para>
+       <function>PQsetSSLKeyPassHook_nss</function> lets an application override
+       <application>libpq</application>'s default handling of password protected
+       objects in the <application>NSS</application> database using
+       <xref linkend="libpq-connect-sslpassword"/> or interactive prompting.
+
+<synopsis>
+void PQsetSSLKeyPassHook_nss(PQsslKeyPassHook_nss_type hook);
+</synopsis>
+
+       The application passes a pointer to a callback function with signature:
+<programlisting>
+char *callback_fn(PK11SlotInfo *slot, PRBool retry, void *arg);
+</programlisting>
+       which <application>libpq</application> will call
+       <emphasis>instead of</emphasis> its default
+       <function>PQdefaultSSLKeyPassHook_nss</function> password handler. The
+       callback should determine the password for the token and return a
+       pointer to it. The returned pointer should be allocated with the NSS
+       <function>PORT_Strdup</function> function. The token for which the
+       password is requested is recorded in the slot anc can be identified by
+       calling <function>PK11_GetTokenName</function>. If no password is
+       known, the callback should return <literal>NULL</literal>. The memory
+       will be owned by <productname>NSS</productname> and should not be
+       freed.
+      </para>
+
+      <para>
+        <function>PQsetSSLKeyPassHook_nss</function> has no effect unless
+        the server was compiled with <productname>NSS</productname> support.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry id="libpq-pqgetsslkeypasshook-nss">
+     <term><function>PQgetSSLKeyPassHook_nss</function><indexterm><primary>PQgetSSLKeyPassHook_nss</primary></indexterm></term>
+     <listitem>
+      <para>
+       <function>PQgetSSLKeyPassHook_nss</function> returns the current
+       <productname>NSS</productname> password hook, or <literal>NULL</literal>
+       if none has been set.
+
+<synopsis>
+PQsslKeyPassHook_nss_type PQgetSSLKeyPassHook_nss(void);
+</synopsis>
+      </para>
+
+      <para>
+        <function>PQgetSSLKeyPassHook_nss</function> has no effect unless the
+        server was compiled with <productname>nss</productname> support.
+      </para>
+
      </listitem>
     </varlistentry>
 
@@ -2497,6 +2567,8 @@ void *PQsslStruct(const PGconn *conn, const char *struct_name);
       </para>
       <para>
        The struct(s) available depend on the SSL implementation in use.
+      </para>
+      <para>
        For <productname>OpenSSL</productname>, there is one struct,
        available under the name "OpenSSL", and it returns a pointer to the
        <productname>OpenSSL</productname> <literal>SSL</literal> struct.
@@ -2520,9 +2592,14 @@ void *PQsslStruct(const PGconn *conn, const char *struct_name);
 ]]></programlisting>
       </para>
       <para>
-       This structure can be used to verify encryption levels, check server
-       certificates, and more. Refer to the <productname>OpenSSL</productname>
-       documentation for information about this structure.
+       For <productname>NSS</productname>, there is one struct available under
+       the name "NSS", and it returns a pointer to the
+       <productname>NSS</productname> <literal>PRFileDesc</literal>.
+      </para>
+      <para>
+       These structures can be used to verify encryption levels, check server
+       certificates, and more. Refer to the <acronym>SSL</acronym> library
+       documentation for information about these structures.
       </para>
      </listitem>
     </varlistentry>
@@ -2549,6 +2626,10 @@ void *PQgetssl(const PGconn *conn);
        <xref linkend="libpq-PQsslInUse"/> instead, and for more details about the
        connection, use <xref linkend="libpq-PQsslAttribute"/>.
       </para>
+      <para>
+       This function returns <literal>NULL</literal> when <acronym>SSL</acronym>
+       librariaes other than <productname>OpenSSL</productname> are used.
+      </para>
      </listitem>
     </varlistentry>
 
@@ -7982,6 +8063,11 @@ void PQinitOpenSSL(int do_ssl, int do_crypto);
        before first opening a database connection.  Also be sure that you
        have done that initialization before opening a database connection.
       </para>
+
+      <para>
+       This function does nothing when using <productname>NSS</productname> as
+       the <acronym>SSL</acronym> library.
+      </para>
      </listitem>
     </varlistentry>
 
@@ -8008,6 +8094,11 @@ void PQinitSSL(int do_ssl);
        might be preferable for applications that need to work with older
        versions of <application>libpq</application>.
       </para>
+
+      <para>
+       This function does nothing when using <productname>NSS</productname> as
+       the <acronym>SSL</acronym> library.
+      </para>
      </listitem>
     </varlistentry>
    </variablelist>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index bf877c0e0c..2578ee8c06 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2185,15 +2185,21 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
 
   <indexterm zone="ssl-tcp">
    <primary>SSL</primary>
+   <secondary>TLS</secondary>
   </indexterm>
 
   <para>
    <productname>PostgreSQL</productname> has native support for using
    <acronym>SSL</acronym> connections to encrypt client/server communications
    for increased security. This requires that
-   <productname>OpenSSL</productname> is installed on both client and
+   a supported TLS library is installed on both client and
    server systems and that support in <productname>PostgreSQL</productname> is
    enabled at build time (see <xref linkend="installation"/>).
+   Supported libraries are <productname>OpenSSL</productname> and
+   <productname>NSS</productname>. The terms <acronym>SSL</acronym> and
+   <acronym>TLS</acronym> are often used interchangeably to mean a secure
+   connection using a <acronym>TLS</acronym> protocol, even though
+   <acronym>SSL</acronym> protocols are no longer supported.
   </para>
 
   <sect2 id="ssl-setup">
@@ -2213,8 +2219,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>
 
   <para>
-   To start in <acronym>SSL</acronym> mode, files containing the server certificate
-   and private key must exist.  By default, these files are expected to be
+   To start in <acronym>SSL</acronym> mode, a server certificate
+   and private key must exist. The below sections on the different libraries
+   will discuss how to configure these.
+  </para>
+   
+  <para>
+   By default, these files are expected to be
    named <filename>server.crt</filename> and <filename>server.key</filename>, respectively, in
    the server's data directory, but other names and locations can be specified
    using the configuration parameters <xref linkend="guc-ssl-cert-file"/>
@@ -2304,6 +2315,18 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </note>
   </sect2>
 
+  <sect2 id="ssl-nss-config">
+   <title>NSS Configuration</title>
+
+  <para>
+   <productname>PostgreSQL</productname> will look for certificates and keys
+   in the <productname>NSS</productname> database specified by the parameter
+   <xref linkend="guc-ssl-database"/> in <filename>postgresql.conf</filename>.
+   The parameters for certificate and key filenames are used to identify the
+   nicknames in the database.
+  </para>
+  </sect2>
+
   <sect2 id="ssl-client-certificates">
    <title>Using Client Certificates</title>
 
@@ -2378,7 +2401,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </sect2>
 
   <sect2 id="ssl-server-files">
-   <title>SSL Server File Usage</title>
+   <title>SSL Server File Parameter Usage</title>
 
    <para>
     <xref linkend="ssl-file-usage"/> summarizes the files that are
@@ -2425,6 +2448,14 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       <entry>client certificate must not be on this list</entry>
      </row>
 
+     <row>
+      <entry><xref linkend="guc-ssl-database"/></entry>
+      <entry>certificate database</entry>
+      <entry>contains server certificates, keys and revocation lists; only
+      used when <productname>PostgreSQL</productname> is built with support
+      for <productname>NSS</productname>.</entry>
+     </row>
+
     </tbody>
    </tgroup>
   </table>
@@ -2448,7 +2479,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </sect2>
 
   <sect2 id="ssl-certificate-creation">
-   <title>Creating Certificates</title>
+   <title>Creating Certificates with OpenSSL</title>
 
    <para>
      To create a simple self-signed certificate for the server, valid for 365
@@ -2552,6 +2583,88 @@ openssl x509 -req -in server.csr -text -days 365 \
    </para>
   </sect2>
 
+  <sect2 id="nss-certificate-database">
+   <title>NSS Certificate Databases</title>
+
+   <para>
+    When using <productname>NSS</productname>, all certificates and keys must
+    be loaded into an <productname>NSS</productname> certificate database.
+   </para>
+
+   <para>
+    To create a new <productname>NSS</productname> certificate database and
+    load the certificates created in <xref linkend="ssl-certificate-creation" />,
+    use the following <productname>NSS</productname> commands:
+<programlisting>
+certutil -d "sql:server.db" -N --empty-password
+certutil -d "sql:server.db" -A -n server.crt -i server.crt -t "CT,C,C"
+certutil -d "sql:server.db" -A -n root.crt -i root.crt -t "CT,C,C"
+</programlisting>
+    This will give the certificate the filename as the nickname identifier in
+    the database which is created as <filename>server.db</filename>.
+   </para>
+   <para>
+    Then load the server key, which require converting it to
+    <acronym>PKCS#12</acronym> format using the
+    <productname>OpenSSL</productname> tools:
+<programlisting>
+openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt \
+  -certfile root.crt -passout pass:
+pk12util -i server.pfx -d server.db -W ''
+</programlisting>
+   </para>
+   <para>
+    Finally a certificate revocation list can be loaded with the following
+    commands:
+<programlisting>
+crlutil -I -i server.crl -d server.db -B
+</programlisting>
+   </para>
+  </sect2>
+
+  <sect2 id="nss-ssl-certificate-creation" />
+   <title>Creating Certificates with NSS</title>
+
+   <para>
+    To create a simple self-signed CA and certificate for the server, use the
+    following NSS commands. Replace
+    <replaceable>*.yourdomain.com</replaceable> with the server's host
+    name. Remove <replaceable>--empty-password</replaceable> in order to set
+    a password protecting the databases. The password can be passed to
+    <application>certutil</certutil> with the <literal>-f</literal> parameter.
+    First create a self-signed CA. To use a different certificate validity
+    time than the default, use <replaceable>-v</replaceable> to specify the
+    number of months of validity.
+<programlisting>
+mkdir root_ca.db
+certutil -N -d "sql:root_ca.db/" --empty-password
+certutil -S -d "sql:root_ca.db/" -n root_ca -s "CN=<replaceable>ca.yourdomain.com</replaceable>" \
+  -x -k rsa -g 2048 -m <replaceable>5432</replaceable> -t CTu,CTu,CTu \
+  --keyUsage certSigning -2 --nsCertType sslCA,smimeCA,objectSigningCA \
+  -Z SHA256
+certutil -L -d "sql:root_ca.db/" -n root_ca -a > root_ca.pem
+</programlisting>
+    Now create a server certificate database, and create a certificate signing
+    request for the CA. Then sign the request with the already created CA and
+    insert the certificate chain into the certificate database.
+<programlisting>
+mkdir server_cert.db
+certutil -N -d "sql:server_cert.db/" --empty-password
+certutil -R -d "sql:server_cert.db/" -s "CN=dbhost.yourdomain.com" \
+  -o server_cert.csr -g 2048 -Z SHA256
+
+certutil -C -d "sql:root_ca.db/" -c root_ca -i server_cert.csr \
+  -o server_cert.der -m <replaceable>5433</replaceable> \
+  --keyUsage keyEncipherment,dataEncipherment,digitalSignature \
+  --nsCertType sslServer -Z SHA256
+
+certutil -A -d "sql:server_cert.db/" -n root_ca -t CTu,CTu,CTu -a -i root_ca.pem
+certutil -A -d "sql:server_cert.db/" -n server_cert -t CTu,CTu,CTu -i server_cert.der
+</programlisting>
+    The server certificate is loaded into the certificate database as well as
+    the CA certificate in order to provide the chain of certificates.
+  </sect2>
+
  </sect1>
 
  <sect1 id="gssapi-enc">
-- 
2.21.1 (Apple Git-122.3)

