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