306 lines
8.3 KiB
Diff
306 lines
8.3 KiB
Diff
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
|
|
index fa7b7add93a..c1a4a8427cf 100644
|
|
--- a/src/backend/postmaster/postmaster.c
|
|
+++ b/src/backend/postmaster/postmaster.c
|
|
@@ -1948,6 +1948,7 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
|
|
ProtocolVersion proto;
|
|
MemoryContext oldcontext;
|
|
|
|
+retry:
|
|
pq_startmsgread();
|
|
|
|
/*
|
|
@@ -2080,7 +2081,16 @@ retry1:
|
|
* another SSL negotiation request, and a GSS request should only
|
|
* follow if SSL was rejected (client may negotiate in either order)
|
|
*/
|
|
- return ProcessStartupPacket(port, true, SSLok == 'S');
|
|
+ ssl_done = true;
|
|
+ if (SSLok == 'S')
|
|
+ {
|
|
+ /*
|
|
+ * We are done with SSL and negotiated correctly, so consider the
|
|
+ * same for GSS.
|
|
+ */
|
|
+ gss_done = true;
|
|
+ }
|
|
+ goto retry;
|
|
}
|
|
else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
|
|
{
|
|
@@ -2124,7 +2134,16 @@ retry1:
|
|
* another GSS negotiation request, and an SSL request should only
|
|
* follow if GSS was rejected (client may negotiate in either order)
|
|
*/
|
|
- return ProcessStartupPacket(port, GSSok == 'G', true);
|
|
+ gss_done = true;
|
|
+ if (GSSok == 'G')
|
|
+ {
|
|
+ /*
|
|
+ * We are done with GSS and negotiated correctly, so consider the
|
|
+ * same for SSL.
|
|
+ */
|
|
+ ssl_done = true;
|
|
+ }
|
|
+ goto retry;
|
|
}
|
|
|
|
/* Could add additional special packet types here */
|
|
diff --git a/src/test/Makefile b/src/test/Makefile
|
|
index efb206aa750..65b8d0d8b99 100644
|
|
--- a/src/test/Makefile
|
|
+++ b/src/test/Makefile
|
|
@@ -12,7 +12,7 @@ subdir = src/test
|
|
top_builddir = ../..
|
|
include $(top_builddir)/src/Makefile.global
|
|
|
|
-SUBDIRS = perl regress isolation modules authentication recovery subscription
|
|
+SUBDIRS = perl postmaster regress isolation modules authentication recovery subscription
|
|
|
|
# Test suites that are not safe by default but can be run if selected
|
|
# by the user via the whitespace-separated list in variable
|
|
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
|
|
index 5ba26782ceb..0aaadee553d 100644
|
|
--- a/src/test/perl/PostgresNode.pm
|
|
+++ b/src/test/perl/PostgresNode.pm
|
|
@@ -310,6 +310,82 @@ sub connstr
|
|
|
|
=pod
|
|
|
|
+=item $node->raw_connect()
|
|
+
|
|
+Open a raw TCP or Unix domain socket connection to the server. This is
|
|
+used by low-level protocol and connection limit tests.
|
|
+
|
|
+=cut
|
|
+
|
|
+sub raw_connect
|
|
+{
|
|
+ my ($self) = @_;
|
|
+ my $pgport = $self->port;
|
|
+ my $pghost = $self->host;
|
|
+
|
|
+ my $socket;
|
|
+ if ($TestLib::use_unix_sockets)
|
|
+ {
|
|
+ require IO::Socket::UNIX;
|
|
+ my $path = "$pghost/.s.PGSQL.$pgport";
|
|
+
|
|
+ $socket = IO::Socket::UNIX->new(
|
|
+ Type => SOCK_STREAM(),
|
|
+ Peer => $path,
|
|
+ ) or die "Cannot create socket - $IO::Socket::errstr\n";
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ $socket = IO::Socket::INET->new(
|
|
+ PeerHost => $pghost,
|
|
+ PeerPort => $pgport,
|
|
+ Proto => 'tcp'
|
|
+ ) or die "Cannot create socket - $IO::Socket::errstr\n";
|
|
+ }
|
|
+ return $socket;
|
|
+}
|
|
+
|
|
+=pod
|
|
+
|
|
+=item $node->raw_connect_works()
|
|
+
|
|
+Check if raw_connect() function works on this platform. This should
|
|
+be called to SKIP any tests that require raw_connect().
|
|
+
|
|
+This tries to connect to the server, to test whether it works or not,,
|
|
+so the server is up and running. Otherwise this can return 0 even if
|
|
+there's nothing wrong with raw_connect() itself.
|
|
+
|
|
+Notably, raw_connect() does not work on Unix domain sockets on
|
|
+Strawberry perl 5.26.3.1 on Windows, which we use in Cirrus CI images
|
|
+as of this writing. It dies with "not implemented on this
|
|
+architecture".
|
|
+
|
|
+=cut
|
|
+
|
|
+sub raw_connect_works
|
|
+{
|
|
+ my ($self) = @_;
|
|
+
|
|
+ # If we're using Unix domain sockets, we need a working
|
|
+ # IO::Socket::UNIX implementation.
|
|
+ if ($TestLib::use_unix_sockets)
|
|
+ {
|
|
+ eval {
|
|
+ my $sock = $self->raw_connect();
|
|
+ $sock->close();
|
|
+ };
|
|
+ if ($@ =~ /not implemented/)
|
|
+ {
|
|
+ diag "IO::Socket::UNIX does not work: $@";
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+=pod
|
|
+
|
|
=item $node->group_access()
|
|
|
|
Does the data dir allow group access?
|
|
diff --git a/src/test/postmaster/.gitignore b/src/test/postmaster/.gitignore
|
|
new file mode 100644
|
|
index 00000000000..871e943d50e
|
|
--- /dev/null
|
|
+++ b/src/test/postmaster/.gitignore
|
|
@@ -0,0 +1,2 @@
|
|
+# Generated by test suite
|
|
+/tmp_check/
|
|
diff --git a/src/test/postmaster/Makefile b/src/test/postmaster/Makefile
|
|
new file mode 100644
|
|
index 00000000000..58d2b235830
|
|
--- /dev/null
|
|
+++ b/src/test/postmaster/Makefile
|
|
@@ -0,0 +1,23 @@
|
|
+#-------------------------------------------------------------------------
|
|
+#
|
|
+# Makefile for src/test/postmaster
|
|
+#
|
|
+# Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
|
|
+# Portions Copyright (c) 1994, Regents of the University of California
|
|
+#
|
|
+# src/test/postmaster/Makefile
|
|
+#
|
|
+#-------------------------------------------------------------------------
|
|
+
|
|
+subdir = src/test/postmaster
|
|
+top_builddir = ../../..
|
|
+include $(top_builddir)/src/Makefile.global
|
|
+
|
|
+check:
|
|
+ $(prove_check)
|
|
+
|
|
+installcheck:
|
|
+ $(prove_installcheck)
|
|
+
|
|
+clean distclean maintainer-clean:
|
|
+ rm -rf tmp_check
|
|
diff --git a/src/test/postmaster/README b/src/test/postmaster/README
|
|
new file mode 100644
|
|
index 00000000000..7e47bf5cff0
|
|
--- /dev/null
|
|
+++ b/src/test/postmaster/README
|
|
@@ -0,0 +1,27 @@
|
|
+src/test/postmaster/README
|
|
+
|
|
+Regression tests for postmaster
|
|
+===============================
|
|
+
|
|
+This directory contains a test suite for postmaster's handling of
|
|
+connections, connection limits, and startup/shutdown sequence.
|
|
+
|
|
+
|
|
+Running the tests
|
|
+=================
|
|
+
|
|
+NOTE: You must have given the --enable-tap-tests argument to configure.
|
|
+
|
|
+Run
|
|
+ make check
|
|
+or
|
|
+ make installcheck
|
|
+You can use "make installcheck" if you previously did "make install".
|
|
+In that case, the code in the installation tree is tested. With
|
|
+"make check", a temporary installation tree is built from the current
|
|
+sources and then tested.
|
|
+
|
|
+Either way, this test initializes, starts, and stops a test Postgres
|
|
+cluster.
|
|
+
|
|
+See src/test/perl/README for more info about running these tests.
|
|
diff --git a/src/test/postmaster/t/004_negotiate.pl b/src/test/postmaster/t/004_negotiate.pl
|
|
new file mode 100644
|
|
index 00000000000..f4fbae41137
|
|
--- /dev/null
|
|
+++ b/src/test/postmaster/t/004_negotiate.pl
|
|
@@ -0,0 +1,81 @@
|
|
+# Copyright (c) 2026, PostgreSQL Global Development Group
|
|
+
|
|
+# Test the negotiation of combined SSL and GSS requests. This test
|
|
+# relies on both SSL and GSS requests to be rejected first, followed
|
|
+# by more requests.
|
|
+
|
|
+use strict;
|
|
+use warnings FATAL => 'all';
|
|
+use PostgreSQL::Test::Cluster;
|
|
+use PostgreSQL::Test::Utils;
|
|
+use Test::More;
|
|
+use Time::HiRes qw(usleep);
|
|
+
|
|
+my $node = PostgreSQL::Test::Cluster->new('main');
|
|
+$node->init;
|
|
+$node->append_conf('postgresql.conf', "log_min_messages = debug2");
|
|
+$node->append_conf('postgresql.conf',
|
|
+ "log_connections = 'on'");
|
|
+$node->start;
|
|
+
|
|
+if (!$node->raw_connect_works())
|
|
+{
|
|
+ plan skip_all => "this test requires working raw_connect()";
|
|
+}
|
|
+
|
|
+my $sock = $node->raw_connect();
|
|
+
|
|
+# SSLRequest: packet length followed by NEGOTIATE_SSL_CODE.
|
|
+my $ssl_request = pack("Nnn", 8, 1234, 5679);
|
|
+
|
|
+# GSSENCRequest: packet length followed by NEGOTIATE_GSS_CODE.
|
|
+my $gss_request = pack("Nnn", 8, 1234, 5680);
|
|
+
|
|
+# Send SSLRequest, reject or bypass.
|
|
+$sock->send($ssl_request);
|
|
+my $reply = "";
|
|
+$sock->recv($reply, 1);
|
|
+if ($reply ne 'N')
|
|
+{
|
|
+ $sock->close();
|
|
+ plan skip_all =>
|
|
+ "server accepted SSL; test requires SSL to be rejected";
|
|
+}
|
|
+
|
|
+# Send GSSENCRequest, reject or bypass test.
|
|
+$sock->send($gss_request);
|
|
+$reply = "";
|
|
+$sock->recv($reply, 1);
|
|
+if ($reply ne 'N')
|
|
+{
|
|
+ $sock->close();
|
|
+ plan skip_all =>
|
|
+ "server accepted GSS; test requires GSS to be rejected";
|
|
+}
|
|
+
|
|
+my $log_offset = -s $node->logfile;
|
|
+
|
|
+# Send a second SSLRequest, now that we know that both SSL and GSS have
|
|
+# been rejected for this connection. We are done with both requests, so
|
|
+# extra requests will be rejected and fail with an invalid protocol
|
|
+# version, and the connection should be closed by the server.
|
|
+$sock->send($ssl_request);
|
|
+
|
|
+# Try to read a response, there should be nothing, and certainly not an
|
|
+# extra 'N' message indicating a rejection.
|
|
+$reply = "";
|
|
+my $bytes = $sock->recv($reply, 1024);
|
|
+isnt($reply, 'N',
|
|
+ "server does not re-enter SSL negotiation after SSL+GSS were both tried");
|
|
+
|
|
+$sock->close();
|
|
+$node->wait_for_log(qr/FATAL: .* unsupported frontend protocol 1234.5679/,
|
|
+ $log_offset);
|
|
+
|
|
+# Check extra connection with a simple query.
|
|
+my $result = $node->safe_psql('postgres', 'select 1;');
|
|
+is($result, '1', 'server able to accept connection');
|
|
+
|
|
+$node->stop;
|
|
+
|
|
+done_testing();
|