8000 libpq: Add min/max_protocol_version connection options · postgres/postgres@285613c · GitHub
[go: up one dir, main page]

Skip to content

Commit 285613c

Browse files
committed
libpq: Add min/max_protocol_version connection options
All supported version of the PostgreSQL server send the NegotiateProtocolVersion message when an unsupported minor protocol version is requested by a client. But many other applications that implement the PostgreSQL protocol (connection poolers, or other databases) do not, and the same is true for PostgreSQL server versions older than 9.3. Connecting to such other applications thus fails if a client requests a protocol version different than 3.0. This patch adds a max_protocol_version connection option to libpq that specifies the protocol version that libpq should request from the server. Currently only 3.0 is supported, but that will change in a future commit that bumps the protocol version. Even after that version bump the default will likely stay 3.0 for the time being. Once more of the ecosystem supports the NegotiateProtocolVersion message we might want to change the default to the latest minor version. This also adds the similar min_protocol_version connection option, to allow the client to specify that connecting should fail if a lower protocol version is attempted by the server. This can be used to ensure that certain protocol features are used, which can be particularly useful if those features impact security. Author: Jelte Fennema-Nio <postgres@jeltef.nl> Reviewed-by: Robert Haas <robertmhaas@gmail.com> (earlier versions) Discussion: https://www.postgresql.org/message-id/CAGECzQTfc_O%2BHXqAo5_-xG4r3EFVsTefUeQzSvhEyyLDba-O9w@mail.gmail.com Discussion: https://www.postgresql.org/message-id/CAGECzQRbAGqJnnJJxTdKewTsNOovUt4bsx3NFfofz3m2j-t7tA@mail.gmail.com
1 parent 5070349 commit 285613c

File tree

7 files changed

+315
-12
lines changed

7 files changed

+315
-12
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,54 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
21442144
</listitem>
21452145
</varlistentry>
21462146

2147+
<varlistentry id="libpq-connect-min-protocol-version" xreflabel="min_protocol_version">
2148+
<term><literal>min_protocol_version</literal></term>
2149+
<listitem>
2150+
<para>
2151+
Specifies the minimum protocol version to allow for the connection.
2152+
The default is to allow any version of the
2153+
<productname>PostgreSQL</productname> protocol supported by libpq,
2154+
which currently means <literal>3.0</literal>. If the server
2155+
does not support at least this protocol version the connection will be
2156+
closed.
2157+
</para>
2158+
2159+
<para>
2160+
The current supported values are
2161+
<literal>3.0</literal>, <literal>3.2</literal>,
2162+
and <literal>latest</literal>. The <literal>latest</literal> value is
2163+
equivalent to the latest protocol version supported by the libpq
2164+
version being used, which is currently <literal>3.2</literal>.
2165+
</para>
2166+
</listitem>
2167+
</varlistentry>
2168+
2169+
<varlistentry id="libpq-connect-max-protocol-version" xreflabel="max_protocol_version">
2170+
<term><literal>max_protocol_version</literal></term>
2171+
<listitem>
2172+
<para>
2173+
Specifies the protocol version to request from the server.
2174+
The default is to use version <literal>3.0</literal> of the
2175+
<productname>PostgreSQL</productname> protocol, unless the connection
2176+
string specifies a feature that relies on a higher protocol version,
2177+
in which case the latest version supported by libpq is used. If the
2178+
server does not support the protocol version requested by the client,
2179+
the connection is automatically downgraded to a lower minor protocol
2180+
version that the server supports. After the connection attempt has
2181+
completed you can use <xref linkend="libpq-PQprotocolVersion"/> to
2182+
find out which exact protocol version was negotiated.
2183+
</para>
2184+
2185+
<para>
2186+
The current supported values are
2187+
<literal>3.0</literal>, <literal>3.2</literal>,
2188+
and <literal>latest</literal>. The <literal>latest</literal> value is
2189+
equivalent to the latest protocol version supported by the libpq
2190+
version being used, which is currently <literal>3.2</literal>.
2191+
</para>
2192+
</listitem>
2193+
</varlistentry>
2194+
21472195
<varlistentry id="libpq-connect-ssl-max-protocol-version" xreflabel="ssl_max_protocol_version">
21482196
<term><literal>ssl_max_protocol_version</literal></term>
21492197
<listitem>
@@ -9329,6 +9377,26 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
93299377
linkend="libpq-connect-load-balance-hosts"/> connection parameter.
93309378
</para>
93319379
</listitem>
9380+
9381+
<listitem>
9382+ A3D4
<para>
9383+
<indexterm>
9384+
<primary><envar>PGMINPROTOCOLVERSION</envar></primary>
9385+
</indexterm>
9386+
<envar>PGMINPROTOCOLVERSION</envar> behaves the same as the <xref
9387+
linkend="libpq-connect-min-protocol-version"/> connection parameter.
9388+
</para>
9389+
</listitem>
9390+
9391+
<listitem>
9392+
<para>
9393+
<indexterm>
9394+
<primary><envar>PGMAXPROTOCOLVERSION</envar></primary>
9395+
</indexterm>
9396+
<envar>PGMAXPROTOCOLVERSION</envar> behaves the same as the <xref
9397+
linkend="libpq-connect-max-protocol-version"/> connection parameter.
9398+
</para>
9399+
</listitem>
93329400
</itemizedlist>
93339401
</para>
93349402

doc/src/sgml/protocol.sgml

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,37 @@
1818
</para>
1919

2020
<para>
21-
This document describes version 3.0 of the protocol, implemented in
22-
<productname>PostgreSQL</productname> 7.4 and later. For descriptions
23-
of the earlier protocol versions, see previous releases of the
24-
<productname>PostgreSQL</productname> documentation. A single server
21+
This document describes version 3.2 of the protocol, introduced in
22+
<productname>PostgreSQL</productname> version 18. The server and the libpq
23+
client library are backwards compatible with protocol version 3.0,
24+
implemented in <productname>PostgreSQL</productname> 7.4 and later.
25+
For descriptions of earlier protocol versions, see previous releases of the
26+
<productname>PostgreSQL</productname> documentation.
27+
</para>
28+
29+
<para>
30+
A single server
2531
can support multiple protocol versions. The initial startup-request
2632
message tells the server which protocol version the client is attempting to
2733
use. If the major version requested by the client is not supported by
2834
the server, the connection will be rejected (for example, this would occur
2935
if the client requested protocol version 4.0, which does not exist as of
3036
this writing). If the minor version requested by the client is not
31-
supported by the server (e.g., the client requests version 3.1, but the
37+
supported by the server (e.g., the client requests version 3.2, but the
3238
server supports only 3.0), the server may either reject the connection or
3339
may respond with a NegotiateProtocolVersion message containing the highest
3440
minor protocol version which it supports. The client may then choose either
3541
to continue with the connection using the specified protocol version or
3642
to abort the connection.
3743
</para>
3844

45+
<para>
46+
The protocol negotiation was introduced in
47+
<productname>PostgreSQL</productname> version 9.3.21. Earlier versions would
48+
reject the connection if the client requested a minor version that was not
49+
supported by the server.
50+
</para>
51+
3952
<para>
4053
In order to serve multiple clients efficiently, the server launches
4154
a new <quote>backend</quote> process for each client.
@@ -413,8 +426,14 @@
413426
this message indicates the highest supported minor version. This
414427
message will also be sent if the client requested unsupported protocol
415428
options (i.e., beginning with <literal>_pq_.</literal>) in the
416-
startup packet. This message will be followed by an ErrorResponse or
417-
a message indicating the success or failure of authentication.
429+
startup packet.
430+
</para>
431+
<para>
432+
After this message, the authentication will continue using the version
433+
indicated by the server. If the client does not support the older
434+
version, it should immediately close the connection. If the server
435+
does not send this message, it supports the client's requested
436+
protocol version and all the protocol options.
418437
</para>
419438
</listitem>
420439
</varlistentry>

src/include/libpq/pqcomm.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,10 @@ is_unixsock_path(const char *path)
9191

9292
/*
9393
* The earliest and latest frontend/backend protocol version supported.
94-
* (Only protocol version 3 is currently supported)
9594
*/
9695

9796
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(3,0)
98-
#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,0)
97+
#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,2)
9998

10099
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
101100

src/interfaces/libpq/fe-connect.c

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,16 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
325325
"Require-Auth", "", 14, /* sizeof("scram-sha-256") == 14 */
326326
offsetof(struct pg_conn, require_auth)},
327327

328+
{"min_protocol_version", "PGMINPROTOCOLVERSION",
329+
NULL, NULL,
330+
"Min-Protocol-Version", "", 6, /* sizeof("latest") = 6 */
331+
offsetof(struct pg_conn, min_protocol_version)},
332+
333+
{"max_protocol_version", "PGMAXPROTOCOLVERSION",
334+
NULL, NULL,
335+
"Max-Protocol-Version", "", 6, /* sizeof("latest") = 6 */
336+
offsetof(struct pg_conn, max_protocol_version)},
337+
328338
{"ssl_min_protocol_version", "PGSSLMINPROTOCOLVERSION", "TLSv1.2", NULL,
329339
"SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
330340
offsetof(struct pg_conn, ssl_min_protocol_version)},
@@ -483,6 +493,7 @@ static void pgpassfileWarning(PGconn *conn);
483493
static void default_threadlock(int acquire);
484494
static bool sslVerifyProtocolVersion(const char *version);
485495
static bool sslVerifyProtocolRange(const char *min, const char *max);
496+
static bool pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn, const char *context);
486497

487498

488499
/* global variable because fe-auth.c needs to access it */
@@ -2081,6 +2092,48 @@ pqConnectOptions2(PGconn *conn)
20812092
}
20822093
}
20832094

2095+
if (conn->min_protocol_version)
2096+
{
2097+
if (!pqParseProtocolVersion(conn->min_protocol_version, &conn->min_pversion, conn, "min_protocol_version"))
2098+
{
2099+
conn->status = CONNECTION_BAD;
2100+
return false;
2101+
}
2102+
}
2103+
else
2104+
{
2105+
conn->min_pversion = PG_PROTOCOL_EARLIEST;
2106+
}
2107+
2108+
if (conn->max_protocol_version)
2109+
{
2110+
if (!pqParseProtocolVersion(conn->max_protocol_version, &conn->max_pversion, conn, "max_protocol_version"))
2111+
{
2112+
conn->status = CONNECTION_BAD;
2113+
return false;
2114+
}
2115+
}
2116+
else
2117+
{
2118+
/*
2119+
* To not break connecting to older servers/poolers that do not yet
2120+
* support NegotiateProtocolVersion, default to the 3.0 protocol at
2121+
* least for a while longer. Except when min_protocol_version is set
2122+
* to something larger, then we might as well default to the latest.
2123+
*/
2124+
if (conn->min_pversion > PG_PROTOCOL(3, 0))
2125+
conn->max_pversion = PG_PROTOCOL_LATEST;
2126+
else
2127+
conn->max_pversion = PG_PROTOCOL(3, 0);
2128+
}
2129+
2130+
if (conn->min_pversion > conn->max_pversion)
2131+
{
2132+
conn->status = CONNECTION_BAD;
2133+
libpq_append_conn_error(conn, "min_protocol_version is greater than max_protocol_version");
2134+
return false;
2135+
}
2136+
20842137
/*
20852138
* Resolve special "auto" client_encoding from the locale
20862139
*/
@@ -3084,7 +3137,7 @@ PQconnectPoll(PGconn *conn)
30843137
* must persist across individual connection attempts, but we must
30853138
* reset them when we start to consider a new server.
30863139
*/
3087-
conn->pversion = PG_PROTOCOL(3, 0);
3140+
conn->pversion = conn->max_pversion;
30883141
conn->send_appname = true;
30893142
conn->failed_enc_methods = 0;
30903143
conn->current_enc_method = 0;
@@ -4102,6 +4155,7 @@ PQconnectPoll(PGconn *conn)
41024155

41034156
/* OK, we read the message; mark data consumed */
41044157
pqParseDone(conn, conn->inCursor);
4158+
41054159
goto keep_going;
41064160
}
41074161

@@ -8157,6 +8211,38 @@ pqParseIntParam(const char *value, int *result, PGconn *conn,
81578211
return false;
81588212
}
81598213

8214+
/*
8215+
* Parse and try to interpret "value" as a ProtocolVersion value, and if
8216+
* successful, store it in *result.
8217+
*/
8218+
static bool
8219+
pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn,
8220+
const char *context)
8221+
{
8222+
if (strcmp(value, "latest") == 0)
8223+
{
8224+
*result = PG_PROTOCOL_LATEST;
8225+
return true;
8226+
}
8227+
if (strcmp(value, "3.0") == 0)
8228+
{
8229+
*result = PG_PROTOCOL(3, 0);
8230+
return true;
8231+
}
8232+
8233+
/* 3.1 never existed, we went straight from 3.0 to 3.2 */
8234+
8235+
if (strcmp(value, "3.2") == 0)
8236+
{
8237+
*result = PG_PROTOCOL(3, 2);
8238+
return true;
8239+
}
8240+
8241+
libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
8242+
context, value);
8243+
return false;
8244+
}
8245+
81608246
/*
81618247
* To keep the API consistent, the locking stubs are always provided, even
81628248
* if they are not required.

src/interfaces/libpq/fe-protocol3.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,13 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
14321432
goto failure;
14331433
}
14341434

1435+
/* 3.1 never existed, we went straight from 3.0 to 3.2 */
1436+
if (their_version == PG_PROTOCOL(3, 1))
1437+
{
1438+
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requests downgrade to non-existent 3.1 protocol version");
1439+
goto failure;
1440+
}
1441+
14351442
if (num < 0)
14361443
{
14371444
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported negative number of unsupported parameters");
@@ -1444,6 +1451,17 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
14441451
goto failure;
14451452
}
14461453

1454+
if (their_version < conn->min_pversion)
1455+
{
1456+
libpq_append_conn_error(conn, "server only supports protocol version %d.%d, but min_protocol_version was set to %d.%d",
1457+
PG_PROTOCOL_MAJOR(their_version),
1458+
PG_PROTOCOL_MINOR(their_version),
1459+
PG_PROTOCOL_MAJOR(conn->min_pversion),
1460+
PG_PROTOCOL_MINOR(conn->min_pversion));
1461+
1462+
goto failure;
1463+
}
1464+
14471465
/* the version is acceptable */
14481466
conn->pversion = their_version;
14491467

src/interfaces/libpq/libpq-int.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ struct pg_conn
417417
char *gsslib; /* What GSS library to use ("gssapi" or
418418
* "sspi") */
419419
char *gssdelegation; /* Try to delegate GSS credentials? (0 or 1) */
420+
char *min_protocol_version; /* minimum used protocol version */
421+
char *max_protocol_version; /* maximum used protocol version */
420422
char *ssl_min_protocol_version; /* minimum TLS protocol version */
421423
char *ssl_max_protocol_version; /* maximum TLS protocol version */
422424
char 79CD *target_session_attrs; /* desired session properties */
@@ -539,6 +541,8 @@ struct pg_conn
539541
void *scram_client_key_binary; /* binary SCRAM client key */
540542
size_t scram_server_key_len;
541543
void *scram_server_key_binary; /* binary SCRAM server key */
544+
ProtocolVersion min_pversion; /* protocol version to request */
545+
ProtocolVersion max_pversion; /* protocol version to request */
542546

543547
/* Miscellaneous stuff */
544548
int be_pid; /* PID of backend --- needed for cancels */

0 commit comments

Comments
 (0)
0