From fb33fa42874f81a136ed91f4af377efda878c6b1 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Mon, 8 Feb 2021 23:52:48 +0100
Subject: [PATCH v51 08/11] nss: Support NSS in sslinfo

Since sslinfo to a large extent uses the be_tls_* API this mostly
disables functionality which currently is OpenSSL specific.
---
 contrib/sslinfo/sslinfo.c     | 32 ++++++++++++++
 doc/src/sgml/sslinfo.sgml     | 12 +++++-
 src/test/ssl/sslfiles_nss.mk  | 12 +++++-
 src/test/ssl/t/003_sslinfo.pl | 80 +++++++++++++++++------------------
 4 files changed, 93 insertions(+), 43 deletions(-)

diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 5fd46b9874..25a068fa60 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -9,9 +9,11 @@
 
 #include "postgres.h"
 
+#ifdef USE_OPENSSL
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 #include <openssl/asn1.h>
+#endif
 
 #include "access/htup_details.h"
 #include "funcapi.h"
@@ -32,6 +34,7 @@
 
 PG_MODULE_MAGIC;
 
+#ifdef USE_OPENSSL
 static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
 
@@ -42,6 +45,7 @@ typedef struct
 {
 	TupleDesc	tupdesc;
 } SSLExtensionInfoContext;
+#endif
 
 /*
  * Indicates whether current session uses SSL
@@ -142,6 +146,7 @@ ssl_client_serial(PG_FUNCTION_ARGS)
 }
 
 
+#ifdef USE_OPENSSL
 /*
  * Converts OpenSSL ASN1_STRING structure into text
  *
@@ -293,7 +298,23 @@ ssl_issuer_field(PG_FUNCTION_ARGS)
 	else
 		return result;
 }
+#endif							/* USE_OPENSSL */
 
+#ifdef USE_NSS
+PG_FUNCTION_INFO_V1(ssl_client_dn_field);
+Datum
+ssl_client_dn_field(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(ssl_issuer_field);
+Datum
+ssl_issuer_field(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
+}
+#endif							/* USE_NSS */
 
 /*
  * Returns current client certificate subject as one string
@@ -349,6 +370,7 @@ ssl_issuer_dn(PG_FUNCTION_ARGS)
 }
 
 
+#ifdef USE_OPENSSL
 /*
  * Returns information about available SSL extensions.
  *
@@ -482,3 +504,13 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 	/* All done */
 	SRF_RETURN_DONE(funcctx);
 }
+#endif							/* USE_OPENSSL */
+
+#ifdef USE_NSS
+PG_FUNCTION_INFO_V1(ssl_extension_info);
+Datum
+ssl_extension_info(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
+}
+#endif							/* USE_NSS */
diff --git a/doc/src/sgml/sslinfo.sgml b/doc/src/sgml/sslinfo.sgml
index 2a9c45a111..f3ae2fc3b8 100644
--- a/doc/src/sgml/sslinfo.sgml
+++ b/doc/src/sgml/sslinfo.sgml
@@ -22,7 +22,8 @@
 
  <para>
   This extension won't build at all unless the installation was
-  configured with <literal>--with-ssl=openssl</literal>.
+  configured with SSL support, such as <literal>--with-ssl=openssl</literal>
+  or <literal>--with-ssl=nss</literal>.
  </para>
 
  <sect2>
@@ -208,6 +209,9 @@ emailAddress
      the X.500 and X.509 standards, so you cannot just assign arbitrary
      meaning to them.
     </para>
+    <para>
+     This function is only available when using <productname>OpenSSL</productname>.
+    </para>
     </listitem>
    </varlistentry>
 
@@ -223,6 +227,9 @@ emailAddress
      Same as <function>ssl_client_dn_field</function>, but for the certificate issuer
      rather than the certificate subject.
     </para>
+    <para>
+     This function is only available when using <productname>OpenSSL</productname>.
+    </para>
     </listitem>
    </varlistentry>
 
@@ -238,6 +245,9 @@ emailAddress
      Provide information about extensions of client certificate: extension name,
      extension value, and if it is a critical extension.
     </para>
+    <para>
+     This function is only available when using <productname>OpenSSL</productname>.
+    </para>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/src/test/ssl/sslfiles_nss.mk b/src/test/ssl/sslfiles_nss.mk
index adb7363d63..3b4e2494a2 100644
--- a/src/test/ssl/sslfiles_nss.mk
+++ b/src/test/ssl/sslfiles_nss.mk
@@ -43,7 +43,8 @@ NSSFILES := ssl/nss/client_ca.crt.db \
 	ssl/nss/root+server_ca.crt__root+server.crldir.db \
 	ssl/nss/native_ca-root.db \
 	ssl/nss/native_server-root.db \
-	ssl/nss/native_client-root.db
+	ssl/nss/native_client-root.db \
+	ssl/nss/client_ext.crt__client_ext.key.db
 
 nssfiles: $(NSSFILES)
 
@@ -168,6 +169,15 @@ ssl/nss/client.crt__client.key.db: ssl/client.crt
 	openssl pkcs12 -export -out ssl/nss/client.pfx -inkey ssl/client.key -in ssl/client.crt -certfile ssl/client_ca.crt -passout pass:
 	pk12util -i ssl/nss/client.pfx -d "sql:$@" -W ''
 
+ssl/nss/client_ext.crt__client_ext.key.db: ssl/client_ext.crt
+	$(MKDIR_P) $@
+	certutil -d "sql:$@" -N --empty-password
+	certutil -d "sql:$@" -A -n ssl/client_ext.crt -i ssl/client_ext.crt -t "CT,C,C"
+	certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C"
+	certutil -d "sql:$@" -A -n root+server_ca.crt -i ssl/root+server_ca.crt -t "CT,C,C"
+	openssl pkcs12 -export -out ssl/nss/client_ext.pfx -inkey ssl/client_ext.key -in ssl/client_ext.crt -certfile ssl/client_ca.crt -passout pass:
+	pk12util -i ssl/nss/client_ext.pfx -d "sql:$@" -W ''
+
 # Client certificate with encrypted key, signed by client CA
 ssl/nss/client.crt__client-encrypted-pem.key.db: ssl/client.crt
 	$(MKDIR_P) $@
diff --git a/src/test/ssl/t/003_sslinfo.pl b/src/test/ssl/t/003_sslinfo.pl
index 4dc457e1b6..7d9800474d 100644
--- a/src/test/ssl/t/003_sslinfo.pl
+++ b/src/test/ssl/t/003_sslinfo.pl
@@ -14,18 +14,8 @@ use lib $FindBin::RealBin;
 
 use SSL::Server;
 
-if ($ENV{with_ssl} eq 'openssl')
-{
-	plan tests => 13;
-}
-elsif ($ENV{with_ssl} eq 'nss')
-{
-	plan skip_all => 'SSL not supported by this build';
-}
-else
-{
-	plan skip_all => 'SSL not supported by this build';
-}
+plan skip_all => 'SSL not supported by this build'
+	unless (($ENV{with_ssl} eq 'openssl') || ($ENV{with_ssl} eq 'nss'));
 
 #### Some configuration
 
@@ -68,10 +58,11 @@ configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR,
 # We aren't using any CRL's in this suite so we can keep using server-revoked
 # as server certificate for simple client.crt connection much like how the
 # 001 test does.
-switch_server_cert($node, 'server-revoked');
+switch_server_cert($node, certfile => 'server-revoked', nssdatabase => 'server-revoked.crt__server-revoked.key.db');
 
 $common_connstr =
   "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR " .
+  "ssldatabase=ssl/nss/client_ext.crt__client_ext.key.db " .
   "user=ssltestuser sslcert=ssl/client_ext.crt sslkey=$client_tmp_key";
 
 # Make sure we can connect even though previous test suites have established this
@@ -110,32 +101,39 @@ $result = $node->safe_psql("certdb",
   connstr => $common_connstr);
 is($result, 't', "ssl_client_serial() compared with pg_stat_ssl");
 
-# Must not use safe_psql since we expect an error here
-$result = $node->psql("certdb", "SELECT ssl_client_dn_field('invalid');",
-  connstr => $common_connstr);
-is($result, '3', "ssl_client_dn_field() for an invalid field");
-
-$result = $node->safe_psql("trustdb", "SELECT ssl_client_dn_field('commonName');",
-  connstr => "sslrootcert=ssl/root+server_ca.crt sslmode=require " .
-  "dbname=trustdb hostaddr=$SERVERHOSTADDR user=ssltestuser");
-is($result, '', "ssl_client_dn_field() for connection without cert");
-
-$result = $node->safe_psql("certdb",
-  "SELECT '/CN=' || ssl_client_dn_field('commonName') = client_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();",
-  connstr => $common_connstr);
-is($result, 't', "ssl_client_dn_field() for commonName");
-
-$result = $node->safe_psql("certdb",
-  "SELECT ssl_issuer_dn() = issuer_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();",
-  connstr => $common_connstr);
-is($result, 't', "ssl_issuer_dn() for connection with cert");
-
-$result = $node->safe_psql("certdb",
-  "SELECT '/CN=' || ssl_issuer_field('commonName') = issuer_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();",
-  connstr => $common_connstr);
-is($result, 't', "ssl_issuer_field() for commonName");
+SKIP:
+{
+	skip "Functionality not implemented with NSS", 6 if ($ENV{with_ssl} eq 'nss');
+
+	# Must not use safe_psql since we expect an error here
+	$result = $node->psql("certdb", "SELECT ssl_client_dn_field('invalid');",
+	  connstr => $common_connstr);
+	is($result, '3', "ssl_client_dn_field() for an invalid field");
+
+	$result = $node->safe_psql("trustdb", "SELECT ssl_client_dn_field('commonName');",
+	  connstr => "sslrootcert=ssl/root+server_ca.crt sslmode=require " .
+	  "dbname=trustdb hostaddr=$SERVERHOSTADDR user=ssltestuser");
+	is($result, '', "ssl_client_dn_field() for connection without cert");
+
+	$result = $node->safe_psql("certdb",
+	  "SELECT '/CN=' || ssl_client_dn_field('commonName') = client_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();",
+	  connstr => $common_connstr);
+	is($result, 't', "ssl_client_dn_field() for commonName");
+
+	$result = $node->safe_psql("certdb",
+	  "SELECT ssl_issuer_dn() = issuer_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();",
+	  connstr => $common_connstr);
+	is($result, 't', "ssl_issuer_dn() for connection with cert");
+
+	$result = $node->safe_psql("certdb",
+	  "SELECT '/CN=' || ssl_issuer_field('commonName') = issuer_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();",
+	  connstr => $common_connstr);
+	is($result, 't', "ssl_issuer_field() for commonName");
+
+	$result = $node->safe_psql("certdb",
+	  "SELECT value, critical FROM ssl_extension_info() WHERE name = 'basicConstraints';",
+	  connstr => $common_connstr);
+	is($result, 'CA:FALSE|t', 'extract extension from cert');
+}
 
-$result = $node->safe_psql("certdb",
-  "SELECT value, critical FROM ssl_extension_info() WHERE name = 'basicConstraints';",
-  connstr => $common_connstr);
-is($result, 'CA:FALSE|t', 'extract extension from cert');
+done_testing();
-- 
2.24.3 (Apple Git-128)

