With Letsencrypt now protecting web servers left and right, and it makes
sense to me to just re-use the cert that the server may already have
installed.

I've tested this on debian with the client compiled from the master branch,
against a 13.3 server.

This is my first patch to postgresql, so I apologize for any process
errors. I tried to follow
https://wiki.postgresql.org/wiki/Submitting_a_Patch

Hope this list takes attachments.

-- 
typedef struct me_s {
 char name[]      = { "Thomas Habets" };
 char email[]     = { "tho...@habets.se <tho...@habets.pp.se>" };
 char kernel[]    = { "Linux" };
 char *pgpKey[]   = { "http://www.habets.pp.se/pubkey.txt"; };
 char pgp[] = { "9907 8698 8A24 F52F 1C2E  87F6 39A4 9EEA 460A 0169" };
 char coolcmd[]   = { "echo '. ./_&. ./_'>_;. ./_" };
} me_t;
From 10926bff605d1a5d4d807432c944c02d97a45c1b Mon Sep 17 00:00:00 2001
From: Thomas Habets <hab...@google.com>
Date: Mon, 6 Sep 2021 16:34:19 +0100
Subject: [PATCH] Add sslmode=verify-system, using default CAs

---
 doc/src/sgml/libpq.sgml                  | 99 ++++++++++++++++--------
 doc/src/sgml/runtime.sgml                |  3 +-
 src/interfaces/libpq/fe-connect.c        | 12 +--
 src/interfaces/libpq/fe-secure-common.c  |  3 +-
 src/interfaces/libpq/fe-secure-openssl.c | 19 ++++-
 5 files changed, 93 insertions(+), 43 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b449c834a9..34686bf283 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1099,12 +1099,12 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
        </para>
 
        <para>
-        Using <literal>hostaddr</literal> allows the
-        application to avoid a host name look-up, which might be important
-        in applications with time constraints. However, a host name is
-        required for GSSAPI or SSPI authentication
-        methods, as well as for <literal>verify-full</literal> SSL
-        certificate verification.  The following rules are used:
+        Using <literal>hostaddr</literal> allows the application to avoid a
+        host name look-up, which might be important in applications with time
+        constraints. However, a host name is required for GSSAPI or SSPI
+        authentication methods, as well as for <literal>verify-full</literal>
+        and <literal>verify-system</literal> SSL certificate verification.
+        The following rules are used:
         <itemizedlist>
          <listitem>
           <para>
@@ -1583,7 +1583,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
            </para>
           </listitem>
          </varlistentry>
-        </variablelist>
+
+         <varlistentry>
+          <term><literal>verify-system</literal></term>
+          <listitem>
+           <para>
+            only try an <acronym>SSL</acronym> connection, verify that the
+            server certificate is issued by a
+            trusted <acronym>CA</acronym> in the default system CA list
+           </para>
+          </listitem>
+         </varlistentry>
+</variablelist>
 
         See <xref linkend="libpq-ssl"/> for a detailed description of how
         these options work.
@@ -1593,13 +1604,14 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         <literal>sslmode</literal> is ignored for Unix domain socket
         communication.
         If <productname>PostgreSQL</productname> is compiled without SSL support,
-        using options <literal>require</literal>, <literal>verify-ca</literal>, or
-        <literal>verify-full</literal> will cause an error, while
-        options <literal>allow</literal> and <literal>prefer</literal> will be
-        accepted but <application>libpq</application> will not actually attempt
+        using options <literal>require</literal>, <literal>verify-ca</literal>,
+        <literal>verify-full</literal>, or <literal>verify-system</literal>
+        will cause an error, while options <literal>allow</literal>
+        and <literal>prefer</literal> will be accepted
+        but <application>libpq</application> will not actually attempt
         an <acronym>SSL</acronym>
-        connection.<indexterm><primary>SSL</primary><secondary
-        sortas="libpq">with libpq</secondary></indexterm>
+        connection.<indexterm><primary>SSL</primary><secondary sortas="libpq">with
+        libpq</secondary></indexterm>
        </para>
 
        <para>
@@ -8321,29 +8333,31 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
   </para>
 
   <para>
-   Once a chain of trust has been established, there are two ways for
-   the client to validate the leaf certificate sent by the server.
-   If the parameter <literal>sslmode</literal> is set to <literal>verify-ca</literal>,
-   libpq will verify that the server is trustworthy by checking the
-   certificate chain up to the root certificate stored on the client.
-   If <literal>sslmode</literal> is set to <literal>verify-full</literal>,
-   libpq will <emphasis>also</emphasis> verify that the server host
-   name matches the name stored in the server certificate. The
-   SSL connection will fail if the server certificate cannot be
+   Once a chain of trust has been established, there are two ways for the
+   client to validate the leaf certificate sent by the server.  If the
+   parameter <literal>sslmode</literal> is set
+   to <literal>verify-ca</literal>, libpq will verify that the server is
+   trustworthy by checking the certificate chain up to the root certificate
+   stored on the client.  If <literal>sslmode</literal> is set
+   to <literal>verify-full</literal> or <literal>verify-system</literal>,
+   libpq will <emphasis>also</emphasis> verify that the server host name
+   matches the name stored in the server certificate. The SSL connection will
+   fail if the server certificate cannot be
    verified. <literal>verify-full</literal> is recommended in most
    security-sensitive environments.
   </para>
 
   <para>
-   In <literal>verify-full</literal> mode, the host name is matched against the
-   certificate's Subject Alternative Name attribute(s), or against the
-   Common Name attribute if no Subject Alternative Name of type <literal>dNSName</literal> is
-   present.  If the certificate's name attribute starts with an asterisk
-   (<literal>*</literal>), the asterisk will be treated as
-   a wildcard, which will match all characters <emphasis>except</emphasis> a dot
-   (<literal>.</literal>). This means the certificate will not match subdomains.
-   If the connection is made using an IP address instead of a host name, the
-   IP address will be matched (without doing any DNS lookups).
+   In <literal>verify-full</literal> and <literal>verify-system</literal>
+   modes, the host name is matched against the certificate's Subject
+   Alternative Name attribute(s), or against the Common Name attribute if no
+   Subject Alternative Name of type <literal>dNSName</literal> is present.  If
+   the certificate's name attribute starts with an asterisk
+   (<literal>*</literal>), the asterisk will be treated as a wildcard, which
+   will match all characters <emphasis>except</emphasis> a dot
+   (<literal>.</literal>). This means the certificate will not match
+   subdomains.  If the connection is made using an IP address instead of a
+   host name, the IP address will be matched (without doing any DNS lookups).
   </para>
 
   <para>
@@ -8378,7 +8392,8 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
     as that of <literal>verify-ca</literal>, meaning the server certificate
     is validated against the CA. Relying on this behavior is discouraged,
     and applications that need certificate validation should always use
-    <literal>verify-ca</literal> or <literal>verify-full</literal>.
+    <literal>verify-ca</literal>, <literal>verify-full</literal>,
+    or <literal>verify-system</literal>.
    </para>
   </note>
  </sect2>
@@ -8500,7 +8515,8 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
    by setting the <literal>sslmode</literal> parameter to <literal>verify-full</literal> or
    <literal>verify-ca</literal>, and providing the system with a root certificate to
    verify against. This is analogous to using an <literal>https</literal>
-   <acronym>URL</acronym> for encrypted web browsing.
+   <acronym>URL</acronym> for encrypted web browsing. <literal>verify-system</literal>
+   works exactly like encrypted web browser, with public certificate authorities.
   </para>
 
   <para>
@@ -8586,7 +8602,17 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
        <entry>Yes</entry>
        <entry>I want my data encrypted, and I accept the overhead. I want to be
         sure that I connect to a server I trust, and that it's the one I
-        specify.
+        specify. I supply the CA certificate.
+       </entry>
+      </row>
+
+     <row>
+      <entry><literal>verify-system</literal></entry>
+       <entry>Yes</entry>
+       <entry>Yes</entry>
+       <entry>I want my data encrypted, and I accept the overhead. I want to be
+        sure that I connect to a server I trust, and that it's the one I
+        specify. The default system CA set verifies the identity of the server.
        </entry>
       </row>
 
@@ -8604,6 +8630,11 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
    <literal>verify-ca</literal> often provides enough protection.
   </para>
 
+  <para>
+   The difference between <literal>verify-full</literal>
+   and <literal>verify-system<literal> is only which CA is used.
+  </para>
+
   <para>
    The default value for <literal>sslmode</literal> is <literal>prefer</literal>. As is shown
    in the table, this makes no sense from a security point of view, and it only
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index f1cbc1d9e9..4a01ed9a56 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2015,7 +2015,8 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    (<xref linkend="ssl-tcp"/>). The TCP client must connect using
    <literal>sslmode=verify-ca</literal> or
    <literal>verify-full</literal> and have the appropriate root certificate
-   file installed (<xref linkend="libq-ssl-certificates"/>).
+   file installed (<xref linkend="libq-ssl-certificates"/>). Alternatively the
+   system CA pool can be used using <literal>sslmode=verify-system</literal>.
   </para>
 
   <para>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 49eec3e835..e739adf50f 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -271,7 +271,7 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 	 * to exclude them since none of them are mandatory.
 	 */
 	{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
-		"SSL-Mode", "", 12,		/* sizeof("verify-full") == 12 */
+		"SSL-Mode", "", 12,		/* sizeof("verify-system") == 14 */
 	offsetof(struct pg_conn, sslmode)},
 
 	{"sslcompression", "PGSSLCOMPRESSION", "0", NULL,
@@ -1283,7 +1283,8 @@ connectOptions2(PGconn *conn)
 			&& strcmp(conn->sslmode, "prefer") != 0
 			&& strcmp(conn->sslmode, "require") != 0
 			&& strcmp(conn->sslmode, "verify-ca") != 0
-			&& strcmp(conn->sslmode, "verify-full") != 0)
+			&& strcmp(conn->sslmode, "verify-full") != 0
+			&& strcmp(conn->sslmode, "verify-system") != 0)
 		{
 			conn->status = CONNECTION_BAD;
 			appendPQExpBuffer(&conn->errorMessage,
@@ -1305,7 +1306,7 @@ connectOptions2(PGconn *conn)
 				break;
 
 			case 'r':			/* "require" */
-			case 'v':			/* "verify-ca" or "verify-full" */
+			case 'v':			/* "verify-ca", "verify-full", or "verify-system" */
 				conn->status = CONNECTION_BAD;
 				appendPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"),
@@ -3053,8 +3054,9 @@ keep_going:						/* We will come back to here until there is
 						conn->inStart = conn->inCursor;
 						/* OK to do without SSL? */
 						if (conn->sslmode[0] == 'r' ||	/* "require" */
-							conn->sslmode[0] == 'v')	/* "verify-ca" or
-														 * "verify-full" */
+							conn->sslmode[0] == 'v')	/* "verify-ca",
+														 * "verify-full", or
+														 * "verify-system" */
 						{
 							/* Require SSL, but server does not want it */
 							appendPQExpBufferStr(&conn->errorMessage,
diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c
index afa5d133e1..38b689a600 100644
--- a/src/interfaces/libpq/fe-secure-common.c
+++ b/src/interfaces/libpq/fe-secure-common.c
@@ -161,7 +161,8 @@ pq_verify_peer_name_matches_certificate(PGconn *conn)
 	 * If told not to verify the peer name, don't do it. Return true
 	 * indicating that the verification was successful.
 	 */
-	if (strcmp(conn->sslmode, "verify-full") != 0)
+	if (strcmp(conn->sslmode, "verify-full") != 0
+		&& strcmp(conn->sslmode, "verify-system") != 0)
 		return true;
 
 	/* Check that we have a hostname to compare with. */
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 3a7cc8f774..a677ff29fa 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -919,8 +919,8 @@ initialize_SSL(PGconn *conn)
 
 	/*
 	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
+	 * verification. If sslmode is "verify-full" or "verify-system" we will
+	 * also do further verification after the connection has been completed.
 	 */
 	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
 		strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
@@ -976,6 +976,21 @@ initialize_SSL(PGconn *conn)
 		}
 		have_rootcert = true;
 	}
+	else if (!strcmp(conn->sslmode, "verify-system"))
+	{
+		if (SSL_CTX_set_default_verify_paths(SSL_context) != 1)
+		{
+			char	   *err = SSLerrmessage(ERR_get_error());
+
+			appendPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not set default root certificate paths: %s\n"),
+							  err);
+			SSLerrfree(err);
+			SSL_CTX_free(SSL_context);
+			return -1;
+		}
+		have_rootcert = true;
+	}
 	else
 	{
 		/*
-- 
2.33.0.153.gba50c8fa24-goog

Reply via email to