Serverside SNI support in libpq

Started by Daniel Gustafssonalmost 2 years ago61 messages
Jump to latest
#1Daniel Gustafsson
daniel@yesql.se

SNI was brought up the discussions around the ALPN work, and I have had asks
for it off-list, so I decided to dust off an old patch I started around the
time we got client-side SNI support but never finished (until now). Since
there is discussion and thinking around how we handle SSL right now I wanted to
share this early even though it will be parked in the July CF for now. There
are a few usecases for serverside SNI, allowing for completely disjoint CAs for
different hostnames is one that has come up. Using strict SNI mode (elaborated
on below) as a cross-host attack mitigation was mentioned in [0]/messages/by-id/e782e9f4-a0cd-49f5-800b-5e32a1b29183@eisentraut.org.

The attached patch adds serverside SNI support to libpq, it is still a bit
rough around the edges but I'm sharing it early to make sure I'm not designing
it in a direction that the community doesn't like. A new config file
$datadir/pg_hosts.conf is used for configuring which certicate and key should
be used for which hostname. The file is parsed in the same way as pg_ident
et.al so it allows for the usual include type statements we support. A new
GUC, ssl_snimode, is added which controls how the hostname TLS extension is
handled. The possible values are off, default and strict:

- off: pg_hosts.conf is not parsed and the hostname TLS extension is
not inspected at all. The normal SSL GUCs for certificates and keys
are used.
- default: pg_hosts.conf is loaded as well as the normal GUCs. If no
match for the TLS extension hostname is found in pg_hosts the cert
and key from the postgresql.conf GUCs is used as the default (used
as a wildcard host).
- strict: only pg_hosts.conf is loaded and the TLS extension hostname
MUST be passed and MUST have a match in the configuration, else the
connection is refused.

As of now the patch use default as the initial value for the GUC.

The way multiple certificates are handled is that libpq creates one SSL_CTX for
each at startup, and switch to the appropriate one when the connection is
inspected. Configuration handling is done in secure-common to not tie it to a
specific TLS backend (should we ever support more), but the validation of the
config values is left for the TLS backend.

There are a few known open items with this patch:

* There are two OpenSSL callbacks which can be used to inspect the hostname TLS
extension: SSL_CTX_set_tlsext_servername_callback and
SSL_CTX_set_client_hello_cb. The documentation for the latter says you
shouldn't use the former, and the docs for the former says you need it even if
you use the latter. For now I'm using SSL_CTX_set_tlsext_servername_callback
mainly because the OpenSSL tools themselves use that for SNI.

* The documentation is not polished at all and will require a more work to make
it passable I think. There are also lot's more testing that can be done, so
far it's pretty basic.

* I've so far only tested with OpenSSL and haven't yet verified how LibreSSL
handles this.

--
Daniel Gustafsson

[0]: /messages/by-id/e782e9f4-a0cd-49f5-800b-5e32a1b29183@eisentraut.org

Attachments:

v1-0001-POC-serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v1-0001-POC-serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+673-42
#2Cary Huang
cary.huang@highgo.ca
In reply to: Daniel Gustafsson (#1)
Re: Serverside SNI support in libpq

The following review has been posted through the commitfest application:
make installcheck-world: not tested
Implements feature: not tested
Spec compliant: not tested
Documentation: not tested

This is an interesting feature on PostgreSQL server side where it can swap the
certificate settings based on the incoming hostnames in SNI field in client
hello message.

I think this patch resonate with a patch I shared awhile ago
( https://commitfest.postgresql.org/48/4924/ ) that adds multiple certificate
support on the libpq client side while this patch adds multiple certificate
support on the server side. My patch allows user to supply multiple certs, keys,
sslpasswords in comma separated format and the libpq client will pick one that
matches the CA issuer names sent by the server. In relation with your patch,
this CA issuer name would match the CA certificate configured in pg_hosts.cfg.

I had a look at the patch and here's my comments:

+   <para>
+    <productname>PostgreSQL</productname> can be configured for
+    <acronym>SNI</acronym> using the <filename>pg_hosts.conf</filename>
+    configuration file. <productname>PostgreSQL</productname> inspects the TLS
+    hostname extension in the SSL connection handshake, and selects the right
+    SSL certificate, key and CA certificate to use for the connection.
+   </para>

pg_hosts should also have sslpassword_command just like in the postgresql.conf in
case the sslkey for a particular host is encrypted with a different password.

+	/*
+	 * Install SNI TLS extension callback in case the server is configured to
+	 * validate hostnames.
+	 */
+	if (ssl_snimode != SSL_SNIMODE_OFF)
+		SSL_CTX_set_tlsext_servername_callback(context, sni_servername_cb);

If libpq client does not provide SNI, this callback will not be called, so there
is not a chance to check for a hostname match from pg_hosts, swap the TLS CONTEXT,
or possibly reject the connection even in strict mode. The TLS handshake in such
case shall proceed and server will use the certificate specified in
postgresql.conf (if these are loaded) to complete the handshake with the client.
There is a comment in the patch that reads:

- strict: only pg_hosts.conf is loaded and the TLS extension hostname
MUST be passed and MUST have a match in the configuration, else the
connection is refused.

I am not sure if it implies that if ssl_snimode is strict, then the normal ssl_cert,
ssl_key and ca_cert…etc settings in postgresql.conf are ignored?

thank you

Cary Huang
-------------
HighGo Software Inc. (Canada)
cary.huang@highgo.ca
www.highgo.ca

#3Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#1)
Re: Serverside SNI support in libpq

On Fri, May 10, 2024 at 7:23 AM Daniel Gustafsson <daniel@yesql.se> wrote:

The way multiple certificates are handled is that libpq creates one SSL_CTX for
each at startup, and switch to the appropriate one when the connection is
inspected.

I fell in a rabbit hole while testing this patch, so this review isn't
complete, but I don't want to delay it any more. I see a few
possibly-related problems with the handling of SSL_context.

The first is that reloading the server configuration doesn't reset the
contexts list, so the server starts behaving in really strange ways
the longer you test. That's an easy enough fix, but things got weirder
when I did. Part of that weirdness is that SSL_context gets set to the
last initialized context, so fallback doesn't always behave in a
deterministic fashion. But we do have to set it to something, to
create the SSL object itself...

I tried patching all that, but I continue to see nondeterministic
behavior, including the wrong certificate chain occasionally being
served, and the servername callback being called twice for each
connection (?!).

Since I can't reproduce the weirdest bits under a debugger yet, I
don't really know what's happening. Maybe my patches are buggy. Or
maybe we're running into some chicken-and-egg madness? The order of
operations looks like this:

1. Create a list of contexts, selecting one as an arbitrary default
2. Create an SSL object from our default context
3. During the servername_callback, reparent that SSL object (which has
an active connection underway) to the actual context we want to use
4. Complete the connection

It's step 3 that I'm squinting at. I wondered how, exactly, that
worked in practice, and based on this issue the answer might be "not
well":

https://github.com/openssl/openssl/issues/6109

Matt Caswell appears to be convinced that SSL_set_SSL_CTX() is
fundamentally broken. So it might just be FUD, but I'm wondering if we
should instead be using the SSL_ flavors of the API to reassign the
certificate chain on the SSL pointer directly, inside the callback,
instead of trying to set them indirectly via the SSL_CTX_ API.

Have you seen any weird behavior like this on your end? I'm starting
to doubt my test setup... On the plus side, I now have a handful of
debugging patches for a future commitfest.

Thanks,
--Jacob

#4Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Cary Huang (#2)
Re: Serverside SNI support in libpq

On Fri, May 24, 2024 at 12:55 PM Cary Huang <cary.huang@highgo.ca> wrote:

pg_hosts should also have sslpassword_command just like in the postgresql.conf in
case the sslkey for a particular host is encrypted with a different password.

Good point. There is also the HBA-related handling of client
certificate settings (such as pg_ident)...

I really dislike that these things are governed by various different
files, but I also feel like I'm opening up a huge can of worms by
requesting nestable configurations.

+       if (ssl_snimode != SSL_SNIMODE_OFF)
+               SSL_CTX_set_tlsext_servername_callback(context, sni_servername_cb);

If libpq client does not provide SNI, this callback will not be called, so there
is not a chance to check for a hostname match from pg_hosts, swap the TLS CONTEXT,
or possibly reject the connection even in strict mode.

I'm mistrustful of my own test setup (see previous email to the
thread), but I don't seem to be able to reproduce this. With sslsni=0
set, strict mode correctly shuts down the connection for me. Can you
share your setup?

(The behavior you describe might be a useful setting in practice, to
let DBAs roll out strict protection for new clients gracefully without
immediately blocking older ones.)

Thanks,
--Jacob

#5Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#3)
Re: Serverside SNI support in libpq

On 25 Jul 2024, at 19:51, Jacob Champion <jacob.champion@enterprisedb.com> wrote:

The attached rebased version adds proper list reset, a couple of bugfixes
around cert loading and the ability to set ssl_passhprase_command (and reload)
in the hosts file.

Matt Caswell appears to be convinced that SSL_set_SSL_CTX() is
fundamentally broken. So it might just be FUD, but I'm wondering if we
should instead be using the SSL_ flavors of the API to reassign the
certificate chain on the SSL pointer directly, inside the callback,
instead of trying to set them indirectly via the SSL_CTX_ API.

Maybe, but I would feel better about changing if I can could reproduce the
issues (see below).

Have you seen any weird behavior like this on your end? I'm starting
to doubt my test setup...

I've not been able to reproduce any behaviour like what you describe.

On the plus side, I now have a handful of
debugging patches for a future commitfest.

Do you have them handy for running tests on this version?

--
Daniel Gustafsson

Attachments:

v2-0001-Serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v2-0001-Serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+798-49
#6Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#5)
Re: Serverside SNI support in libpq

On Tue, Dec 3, 2024 at 5:58 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Have you seen any weird behavior like this on your end? I'm starting
to doubt my test setup...

I've not been able to reproduce any behaviour like what you describe.

Hm, v2 is different enough that I'm going to need to check my notes
and try to reproduce again. At first glance, I am still seeing strange
reload behavior (e.g. issuing `pg_ctl reload` a couple of times in a
row leads to the server disappearing without any log messages
indicating why).

On the plus side, I now have a handful of
debugging patches for a future commitfest.

Do you have them handy for running tests on this version?

I'll work on cleaning them up. I'd meant to contribute them
individually by now, but I got a bit sidetracked...

Thanks!
--Jacob

#7Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#6)
Re: Serverside SNI support in libpq

On 4 Dec 2024, at 01:43, Jacob Champion <jacob.champion@enterprisedb.com> wrote:

On Tue, Dec 3, 2024 at 5:58 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Have you seen any weird behavior like this on your end? I'm starting
to doubt my test setup...

I've not been able to reproduce any behaviour like what you describe.

Hm, v2 is different enough that I'm going to need to check my notes
and try to reproduce again. At first glance, I am still seeing strange
reload behavior (e.g. issuing `pg_ctl reload` a couple of times in a
row leads to the server disappearing without any log messages
indicating why).

On the plus side, I now have a handful of
debugging patches for a future commitfest.

Do you have them handy for running tests on this version?

I'll work on cleaning them up. I'd meant to contribute them
individually by now, but I got a bit sidetracked...

No worries, I know you have a big path on your plate right now. The attached
v3 fixes a small buglet in the tests and adds silly reload testing to try and
stress out any issues.

--
Daniel Gustafsson

Attachments:

v3-0001-Serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v3-0001-Serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+843-49
#8Michael Paquier
michael@paquier.xyz
In reply to: Daniel Gustafsson (#7)
Re: Serverside SNI support in libpq

On Wed, Dec 04, 2024 at 02:44:18PM +0100, Daniel Gustafsson wrote:

No worries, I know you have a big path on your plate right now. The attached
v3 fixes a small buglet in the tests and adds silly reload testing to try and
stress out any issues.

Looks like this still fails quite heavily in the CI.. You may want to
look at that.
--
Michael

#9Daniel Gustafsson
daniel@yesql.se
In reply to: Michael Paquier (#8)
Re: Serverside SNI support in libpq

On 11 Dec 2024, at 01:34, Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Dec 04, 2024 at 02:44:18PM +0100, Daniel Gustafsson wrote:

No worries, I know you have a big path on your plate right now. The attached
v3 fixes a small buglet in the tests and adds silly reload testing to try and
stress out any issues.

Looks like this still fails quite heavily in the CI.. You may want to
look at that.

Interestingly enough the CFBot hasn't picked up that there are new version
posted and the buildfailure is from the initial patch in the thread, which no
longer applies (as the CFBot righly points out). I'll try posting another
version later today to see if that gets it unstuck.

--
Daniel Gustafsson

#10Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#9)
Re: Serverside SNI support in libpq

Attached is a rebase which fixes a few smaller things (and a pgperltidy run);
and adds a paragraph to the docs about how HBA clientname settings can't be
made per certificate set in an SNI config. As discussed with Jacob offlist,
there might be a case for supporting that but it will be a niche usecase within
a niche feature, so rather than complicating the code for something which might
never be used, it's likely better to document it and await feedback.

Are there any blockers for getting this in?

--
Daniel Gustafsson

Attachments:

v5-0001-Serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v5-0001-Serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+839-51
#11Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#10)
Re: Serverside SNI support in libpq

On Wed, Feb 19, 2025 at 3:13 PM Daniel Gustafsson <daniel@yesql.se> wrote:

Are there any blockers for getting this in?

+ SSL_context = ssl_init_context(isServerStart, host);

I'm still not quite following the rationale behind the SSL_context
assignment. To maybe illustrate, attached are some tests that I
expected to pass, but don't.

After adding an additional host and reloading the config, the behavior
of the original fallback host seems to change. Am I misunderstanding
the designed fallback behavior, have I misdesigned my test, or is this
a bug?

Thanks,
--Jacob

Attachments:

tests.diff.txttext/plain; charset=US-ASCII; name=tests.diff.txtDownload+26-0
#12Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#11)
Re: Serverside SNI support in libpq

On 24 Feb 2025, at 22:51, Jacob Champion <jacob.champion@enterprisedb.com> wrote:

On Wed, Feb 19, 2025 at 3:13 PM Daniel Gustafsson <daniel@yesql.se> wrote:

Are there any blockers for getting this in?

+ SSL_context = ssl_init_context(isServerStart, host);

I'm still not quite following the rationale behind the SSL_context
assignment. To maybe illustrate, attached are some tests that I
expected to pass, but don't.

After adding an additional host and reloading the config, the behavior
of the original fallback host seems to change. Am I misunderstanding
the designed fallback behavior, have I misdesigned my test, or is this
a bug?

Thanks for the tests, they did in fact uncover a bug in how fallback was
handled which is now fixed. In doing so I revamped how the default context
handling is done, it now always use the GUCs in postgresql.conf for
consistency. The attached v6 rebase contains this as well as your tests as
well as general cleanup and comment writing etc.

--
Daniel Gustafsson

Attachments:

v6-0001-Serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v6-0001-Serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+937-51
#13Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#12)
Re: Serverside SNI support in libpq

On Thu, Feb 27, 2025 at 5:38 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Thanks for the tests, they did in fact uncover a bug in how fallback was
handled which is now fixed. In doing so I revamped how the default context
handling is done, it now always use the GUCs in postgresql.conf for
consistency. The attached v6 rebase contains this as well as your tests as
well as general cleanup and comment writing etc.

Great, thanks!

Revisiting my concerns from upthread:

On Thu, Jul 25, 2024 at 10:51 AM Jacob Champion
<jacob.champion@enterprisedb.com> wrote:

I tried patching all that, but I continue to see nondeterministic
behavior, including the wrong certificate chain occasionally being
served, and the servername callback being called twice for each
connection (?!).

1) The wrong chain being served was due to the fallback bug, now fixed.
2) The servername callback happening twice is due to the TLS 1.3
HelloRetryRequest problem with our ssl_groups (which reminded me to
ping that thread [1]/messages/by-id/CAOYmi+nTwu7=aUGCkf6L-ULqS8itNP7uc9nUmNLOvbXf2TCgBA@mail.gmail.com). Switching to TLSv1.2 in order to more easily
see the handshake on the wire makes the problem go away, which
probably did not help my sense of growing insanity last July.

https://github.com/openssl/openssl/issues/6109

Matt Caswell appears to be convinced that SSL_set_SSL_CTX() is
fundamentally broken.

We briefly talked about this in Brussels, and I've been trying to find
proof. Attached are some (very rough) tests that might highlight an
issue.

Basically, the new tests set up three hosts in pg_hosts.conf: one with
no client CA, one with a valid client CA, and one with a
malfunctioning CA (root+server_ca, which can't verify our client
certs). Then it switches out the default CA underneath to make sure it
does not affect the visible behavior, since that CA should not
actually be used in the end.

Unfortunately, the failure modes change depending on the default CA.
If it's not a bug in my tests, I think this may be an indication that
SSL_set_SSL_CTX() isn't fully switching out the client verification
behavior? For example, if the default CA isn't set, the other hosts
don't appear to ask for a client certificate even if they need one.
And vice versa.

--

+           /*
+            * Set flag to remember whether CA store has been loaded into this
+            * SSL_context.
+            */
+           if (host->ssl_ca)

I think this should be `if (host->ssl_ca[0])` -- which, incidentally,
fixes one of the new failing tests on my machine.

int
be_tls_init(bool isServerStart)
+{
+   SSL_CTX    *ctx;
+   List       *sni_hosts = NIL;
+   HostsLine   line;

A pointer to `line` is passed down to ssl_init_context(), but it's
only been partially initialized on the stack. Can it be
zero-initialized here instead?

+       if (ssl_snimode == SSL_SNIMODE_STRICT)
+       {
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("no hostname provided in callback")));
+           return SSL_TLSEXT_ERR_ALERT_FATAL;
+       }

At the moment we're sending an `unrecognized_name` alert in strict
mode if the client doesn't send SNI. RFC 8446 suggests
`missing_extension`:

Additionally, all implementations MUST support the use of the
"server_name" extension with applications capable of using it.
Servers MAY require clients to send a valid "server_name" extension.
Servers requiring this extension SHOULD respond to a ClientHello
lacking a "server_name" extension by terminating the connection with
a "missing_extension" alert.

Should we do that, or should we ignore the suggestion? The problem
with missing_extension, IMO, is that there's absolutely no indication
to the client as to which extension is missing. unrecognized_name is a
little confusing in this case (there was no name sent), but at least
the end user will be able to link that to an SNI problem via search
engine.

+#hosts_file = 'ConfigDir/pg_hosts.conf' # hosts configuration file
+                   # (change requires restart)

Nitpickiest nitpick: looks like the other lines use a tab instead of a
space between the setting and the trailing comment.

Thanks,
--Jacob

[1]: /messages/by-id/CAOYmi+nTwu7=aUGCkf6L-ULqS8itNP7uc9nUmNLOvbXf2TCgBA@mail.gmail.com

Attachments:

tests.patch.txttext/plain; charset=US-ASCII; name=tests.patch.txtDownload+64-3
#14Andres Freund
andres@anarazel.de
In reply to: Daniel Gustafsson (#12)
Re: Serverside SNI support in libpq

Hi,

On 2025-02-27 14:38:24 +0100, Daniel Gustafsson wrote:

The attached v6 rebase contains this as well as your tests as well as
general cleanup and comment writing etc.

This is not passing CI on windows...
https://cirrus-ci.com/build/4765059278176256

Greetings,

Andres

#15Daniel Gustafsson
daniel@yesql.se
In reply to: Andres Freund (#14)
Re: Serverside SNI support in libpq

On 13 May 2025, at 15:46, Andres Freund <andres@anarazel.de> wrote:

This is not passing CI on windows...
https://cirrus-ci.com/build/4765059278176256

When looking into why the SNI tests failed on Windows I think I found a
pre-existing issue that we didn't have tests for, which my patch added tests
for and thus broke.

The test I added was to check restarting and reloading with ssl passphrase
commands (which we do have testcoverage for) with a subsequent connection test
to ensure it didn't just work to start the cluster.

When ssl_passphrase_command_supports_reload is set to 'off', the cluster should
allow connections until a reload has been issued. That works fine except on
Windows where our process-model is such that a new connection will re-run the
passphrase command, which inevitably fails as it's not configured for reload.
The test in my patch exposed this out of (happy) accident, but it can be
reproduced in HEAD as well. The attached version modifies the ssl tests to
cover this with a connection attempt. If I'm not mistaken though, there should
probably be a docs patch to make it clear how this works on Windows.

No codechanges on top of the test fix.

--
Daniel Gustafsson

Attachments:

v8-0001-Serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v8-0001-Serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+1007-64
#16Michael Paquier
michael@paquier.xyz
In reply to: Daniel Gustafsson (#15)
Re: Serverside SNI support in libpq

On Wed, Aug 27, 2025 at 09:49:34PM +0200, Daniel Gustafsson wrote:

When looking into why the SNI tests failed on Windows I think I found a
pre-existing issue that we didn't have tests for, which my patch added tests
for and thus broke.

The test I added was to check restarting and reloading with ssl passphrase
commands (which we do have testcoverage for) with a subsequent connection test
to ensure it didn't just work to start the cluster.

Would this part be better if extracted from the main patch and then
backpatched? Even if not backpatched, a split would be cleaner on
HEAD, I assume, leading to less fuzz with the main patch.
--
Michael

#17Daniel Gustafsson
daniel@yesql.se
In reply to: Michael Paquier (#16)
Re: Serverside SNI support in libpq

On 1 Sep 2025, at 03:58, Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Aug 27, 2025 at 09:49:34PM +0200, Daniel Gustafsson wrote:

When looking into why the SNI tests failed on Windows I think I found a
pre-existing issue that we didn't have tests for, which my patch added tests
for and thus broke.

The test I added was to check restarting and reloading with ssl passphrase
commands (which we do have testcoverage for) with a subsequent connection test
to ensure it didn't just work to start the cluster.

Would this part be better if extracted from the main patch and then
backpatched? Even if not backpatched, a split would be cleaner on
HEAD, I assume, leading to less fuzz with the main patch.

Yes, that's my plan, just wanted to float it here first to see if I was
thinking about it all wrong. I will raise it on its own thread on -hackers.
The backpatchable portion is probably limited to a docs entry clarifying the
behaviour on Windows.

--
Daniel Gustafsson

#18Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#17)
Re: Serverside SNI support in libpq

Attached is a cleaned up rebase with improved memory handling, additional code
documentation, removed passphrase test (sent as a separate thread), and some
general cleanup and additional testing.

--
Daniel Gustafsson

Attachments:

v9-0001-Serverside-SNI-support-for-libpq.patchapplication/octet-stream; name=v9-0001-Serverside-SNI-support-for-libpq.patch; x-unix-mode=0644Download+1005-52
#19Chao Li
li.evan.chao@gmail.com
In reply to: Daniel Gustafsson (#18)
Re: Serverside SNI support in libpq

Hi Daniel,

I just reviewed the patch and got a few comments:

On Nov 11, 2025, at 06:32, Daniel Gustafsson <daniel@yesql.se> wrote:

Attached is a cleaned up rebase with improved memory handling, additional code
documentation, removed passphrase test (sent as a separate thread), and some
general cleanup and additional testing.

--
Daniel Gustafsson

<v9-0001-Serverside-SNI-support-for-libpq.patch>

1 - commit message
```
Experimental support for serverside SNI support in libpq, a new config
file $datadir/pg_hosts.conf is used for configuring which certicate and
```

Typo: certicate -> certificate

2 - be-secure-common.c
```
+run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size, void *userdata)
 {
 	int			loglevel = is_server_start ? ERROR : LOG;
 	char	   *command;
 	FILE	   *fh;
 	int			pclose_rc;
 	size_t		len = 0;
+	char	   *cmd = (char *) userdata;
```

Cmd is only passed to replace_percent_placeholders(), and the function take a "const char *” argument, so we can define cmd as “const char *”.

2 - be-secure-common.c
```
+	tokenize_auth_file(HostsFileName, file, &hosts_lines, LOG, 0);
+
+	foreach(line, hosts_lines)
+	{
+		TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
+
+		if (tok_line->err_msg != NULL)
+		{
+			ok = false;
+			continue;
+		}
+
+		if ((newline = parse_hosts_line(tok_line, LOG)) == NULL)
+		{
+			ok = false;
+			continue;
+		}
+
+		parsed_lines = lappend(parsed_lines, newline);
+	}
+
+	free_auth_file(file, 0);
```

When I read this function, I thought to raise a comment for freeing hosts_lines. However, then I read be-secure-openssl.c, I saw the load_hosts() is called within a transient hostctx, so it doesn’t have to free memory pieces. Can we explain that in the function comment? Otherwise other reviewers and future code readers may have the same confusion.

3 - be-secure-openssl.c
```
int
@@ -759,6 +933,9 @@ be_tls_close(Port *port)
pfree(port->peer_dn);
port->peer_dn = NULL;
}
+
+ Host_context = NULL;
+ SSL_context = NULL;
}
```

Do we need to free_contexts() here? When be_tls_init() is called again, contexts will anyway be freed, so I guess earlier free might be better?

4 - guc_parameters.dat
```
+{ name => 'hosts_file', type => 'string', context => 'PGC_POSTMASTER', group => 'FILE_LOCATIONS',
+  short_desc => 'Sets the server\'s "hosts" configuration file.',
+  flags => 'GUC_SUPERUSER_ONLY',
+  variable => 'HostsFileName',
+  boot_val => 'NULL',
+},
+{ name => 'ssl_snimode', type => 'enum', context => 'PGC_SIGHUP', group => 'CONN_AUTH_SSL',
+  short_desc => 'Sets the SNI mode to use for the server.',
+  flags => 'GUC_SUPERUSER_ONLY',
+  variable => 'ssl_snimode',
+  boot_val => 'SSL_SNIMODE_DEFAULT',
+  options => 'ssl_snimode_options',
+},
```

If ssl_snimode is PGC_SIGHUP that allows to reload without a server reset, why hosts_file cannot?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

#20Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#18)
Re: Serverside SNI support in libpq

On Mon, Nov 10, 2025 at 2:33 PM Daniel Gustafsson <daniel@yesql.se> wrote:

Attached is a cleaned up rebase with improved memory handling, additional code
documentation, removed passphrase test (sent as a separate thread), and some
general cleanup and additional testing.

Thanks! Builds and passes back to OpenSSL 1.1.1 and LibreSSL 3.4
(except for the unrelated known issue with "depth 0"/"depth 1", which
this patch did not introduce [1]/messages/by-id/CAOYmi+k=VF-2BCqfR49A92tx=_QNuL=3iT3w6FysOffKw9cxDQ@mail.gmail.com).

Did you have any thoughts on my earlier review [2]? The test patch
attached there still fails on my machine with v9.

Thanks,
--Jacob

[1]: /messages/by-id/CAOYmi+k=VF-2BCqfR49A92tx=_QNuL=3iT3w6FysOffKw9cxDQ@mail.gmail.com
[1]: /messages/by-id/CAOYmi+k=VF-2BCqfR49A92tx=_QNuL=3iT3w6FysOffKw9cxDQ@mail.gmail.com

#21Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Chao Li (#19)
#22Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#20)
#23Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#20)
#24Chao Li
li.evan.chao@gmail.com
In reply to: Daniel Gustafsson (#23)
#25Daniel Gustafsson
daniel@yesql.se
In reply to: Chao Li (#24)
#26Dewei Dai
daidewei1970@163.com
In reply to: Daniel Gustafsson (#5)
#27Daniel Gustafsson
daniel@yesql.se
In reply to: Dewei Dai (#26)
#28Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Daniel Gustafsson (#27)
#29Daniel Gustafsson
daniel@yesql.se
In reply to: Heikki Linnakangas (#28)
#30Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Daniel Gustafsson (#29)
#31Jelte Fennema-Nio
postgres@jeltef.nl
In reply to: Heikki Linnakangas (#30)
#32Daniel Gustafsson
daniel@yesql.se
In reply to: Jelte Fennema-Nio (#31)
#33Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#32)
#34Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#33)
#35Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#34)
#36Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#33)
#37Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Daniel Gustafsson (#36)
#38Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Heikki Linnakangas (#37)
#39Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#36)
#40Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#39)
#41Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#40)
#42Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Jacob Champion (#41)
#43Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#41)
#44Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#43)
#45Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#44)
#46Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#45)
#47Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#46)
#48Zsolt Parragi
zsolt.parragi@percona.com
In reply to: Daniel Gustafsson (#47)
#49Daniel Gustafsson
daniel@yesql.se
In reply to: Zsolt Parragi (#48)
#50Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#49)
#51Zsolt Parragi
zsolt.parragi@percona.com
In reply to: Daniel Gustafsson (#50)
#52Daniel Gustafsson
daniel@yesql.se
In reply to: Zsolt Parragi (#51)
#53Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#52)
#54Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#53)
#55Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#54)
#56Tom Lane
tgl@sss.pgh.pa.us
In reply to: Daniel Gustafsson (#55)
#57Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#56)
#58Tom Lane
tgl@sss.pgh.pa.us
In reply to: Daniel Gustafsson (#57)
#59Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#55)
#60Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#58)
#61Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#59)