Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 58255c0

Browse files
JelteFCommitfest Bot
authored and
Commitfest Bot
committed
libpq: Add min/max_protocol_version connection options
All officially 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 many unspported PostgreSQL server versions. 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 all allowed values result in the use of 3.0, but that will be changed 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. We also add the similar min_protocol_version connection option, to allow a client to specify that connecting should fail if a lower protocol version is attempted by the server. This can be used to ensure certain protocol features are in used, which can be particularly useful if those features impact security.
1 parent cfced9b commit 58255c0

File tree

3 files changed

+184
-1
lines changed

3 files changed

+184
-1
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,6 +2373,56 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
23732373
</para>
23742374
</listitem>
23752375
</varlistentry>
2376+
2377+
<varlistentry id="libpq-connect-min-protocol-version" xreflabel="min_protocol_version">
2378+
<term><literal>min_protocol_version</literal></term>
2379+
<listitem>
2380+
<para>
2381+
Specifies the minimum protocol version to allow for the connection.
2382+
The default is to allow any version of the
2383+
<productname>PostgreSQL</productname> protocol supported by libpq,
2384+
which currently means <literal>3.0</literal>. If the server
2385+
does not support at least this protocol version the connection will be
2386+
closed.
2387+
</para>
2388+
2389+
<para>
2390+
The current supported values are
2391+
<literal>3.0</literal>
2392+
and <literal>latest</literal>. The <literal>latest</literal> value is
2393+
equivalent to the latest protocol version that is supported by the used
2394+
libpq version, which currently is <literal>3.2</literal>, but this will
2395+
change in future libpq releases.
2396+
</para>
2397+
</listitem>
2398+
</varlistentry>
2399+
2400+
<varlistentry id="libpq-connect-max-protocol-version" xreflabel="max_protocol_version">
2401+
<term><literal>max_protocol_version</literal></term>
2402+
<listitem>
2403+
<para>
2404+
Specifies the protocol version to request from the server.
2405+
The default is to use version <literal>3.0</literal> of the
2406+
<productname>PostgreSQL</productname> protocol, unless the connection
2407+
string specifies a feature that relies on a higher protocol version, in
2408+
that case the latest version supported by libpq is used. If the server
2409+
does not support the requested protocol version of the client the
2410+
connection will be automatically downgraded to a lower minor protocol
2411+
version, which the server does support. After the connection attempt has
2412+
completed you can use <xref linkend="libpq-PQprotocolVersion"/> to find
2413+
out which exact protocol version was negotiated.
2414+
</para>
2415+
2416+
<para>
2417+
The current supported values are
2418+
<literal>3.0</literal>
2419+
and <literal>latest</literal>. The <literal>latest</literal> value is
2420+
equivalent to the latest protocol version that is supported by the used
2421+
libpq version, which currently is <literal>3.0</literal>, but this will
2422+
change in future libpq releases.
2423+
</para>
2424+
</listitem>
2425+
</varlistentry>
23762426
</variablelist>
23772427
</para>
23782428
</sect2>
@@ -9219,6 +9269,26 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
92199269
linkend="libpq-connect-load-balance-hosts"/> connection parameter.
92209270
</para>
92219271
</listitem>
9272+
9273+
<listitem>
9274+
<para>
9275+
<indexterm>
9276+
<primary><envar>PGMINPROTOCOLVERSION</envar></primary>
9277+
</indexterm>
9278+
<envar>PGMINPROTOCOLVERSION</envar> behaves the same as the <xref
9279+
linkend="libpq-connect-min-protocol-version"/> connection parameter.
9280+
</para>
9281+
</listitem>
9282+
9283+
<listitem>
9284+
<para>
9285+
<indexterm>
9286+
<primary><envar>PGMAXPROTOCOLVERSION</envar></primary>
9287+
</indexterm>
9288+
<envar>PGMAXPROTOCOLVERSION</envar> behaves the same as the <xref
9289+
linkend="libpq-connect-max-protocol-version"/> connection parameter.
9290+
</para>
9291+
</listitem>
92229292
</itemizedlist>
92239293
</para>
92249294

src/interfaces/libpq/fe-connect.c

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

327+
{"min_protocol_version", "PGMINPROTOCOLVERSION",
328+
NULL, NULL,
329+
"Min-Protocol-Version", "", 6, /* sizeof("latest") = 6 */
330+
offsetof(struct pg_conn, min_protocol_version)},
331+
332+
{"max_protocol_version", "PGMAXPROTOCOLVERSION",
333+
NULL, NULL,
334+
"Max-Protocol-Version", "", 6, /* sizeof("latest") = 6 */
335+
offsetof(struct pg_conn, max_protocol_version)},
336+
327337
{"ssl_min_protocol_version", "PGSSLMINPROTOCOLVERSION", "TLSv1.2", NULL,
328338
"SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
329339
offsetof(struct pg_conn, ssl_min_protocol_version)},
@@ -464,6 +474,7 @@ static void pgpassfileWarning(PGconn *conn);
464474
static void default_threadlock(int acquire);
465475
static bool sslVerifyProtocolVersion(const char *version);
466476
static bool sslVerifyProtocolRange(const char *min, const char *max);
477+
static bool pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn, const char *context);
467478

468479

469480
/* global variable because fe-auth.c needs to access it */
@@ -2056,6 +2067,42 @@ pqConnectOptions2(PGconn *conn)
20562067
}
20572068
}
20582069

2070+
if (conn->min_protocol_version)
2071+
{
2072+
if (!pqParseProtocolVersion(conn->min_protocol_version, &conn->min_pversion, conn, "min_protocol_version"))
2073+
return false;
2074+
}
2075+
else
2076+
{
2077+
conn->min_pversion = PG_PROTOCOL_EARLIEST;
2078+
}
2079+
2080+
if (conn->max_protocol_version)
2081+
{
2082+
if (!pqParseProtocolVersion(conn->max_protocol_version, &conn->max_pversion, conn, "max_protocol_version"))
2083+
return false;
2084+
}
2085+
else
2086+
{
2087+
/*
2088+
* To not break connecting to older servers/poolers that do not yet
2089+
* support NegotiateProtocolVersion, default to the 3.0 protocol at
2090+
* least for a while longer. Except when min_protocol_version is set
2091+
* to something larger, then we might as well default to the latest.
2092+
*/
2093+
if (conn->min_pversion > PG_PROTOCOL(3, 0))
2094+
conn->max_pversion = PG_PROTOCOL_LATEST;
2095+
else
2096+
conn->max_pversion = PG_PROTOCOL(3, 0);
2097+
}
2098+
2099+
if (conn->min_pversion > conn->max_pversion)
2100+
{
2101+
conn->status = CONNECTION_BAD;
2102+
libpq_append_conn_error(conn, "min_protocol_version is greater than max_protocol_version");
2103+
return false;
2104+
}
2105+
20592106
/*
20602107
* Resolve special "auto" client_encoding from the locale
20612108
*/
@@ -3059,7 +3106,7 @@ PQconnectPoll(PGconn *conn)
30593106
* must persist across individual connection attempts, but we must
30603107
* reset them when we start to consider a new server.
30613108
*/
3062-
conn->pversion = PG_PROTOCOL(3, 0);
3109+
conn->pversion = conn->max_pversion;
30633110
conn->send_appname = true;
30643111
conn->failed_enc_methods = 0;
30653112
conn->current_enc_method = 0;
@@ -4073,6 +4120,14 @@ PQconnectPoll(PGconn *conn)
40734120

40744121
/* OK, we read the message; mark data consumed */
40754122
pqParseDone(conn, conn->inCursor);
4123+
4124+
if (conn->pversion < conn->min_pversion)
4125+
{
4126+
libpq_append_conn_error(conn, "server only supports protocol version %d.%d, but min_protocol_version was set to %d.%d", PG_PROTOCOL_MAJOR(conn->pversion), PG_PROTOCOL_MINOR(conn->pversion), PG_PROTOCOL_MAJOR(conn->min_pversion), PG_PROTOCOL_MINOR(conn->min_pversion));
4127+
4128+
goto error_return;
4129+
}
4130+
40764131
goto keep_going;
40774132
}
40784133

@@ -8106,6 +8161,60 @@ pqParseIntParam(const char *value, int *result, PGconn *conn,
81068161
return false;
81078162
}
81088163

8164+
/*
8165+
* Parse and try to interpret "value" as a ProtocolVersion value, and if successful,
8166+
* store it in *result.
8167+
*/
8168+
static bool
8169+
pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn,
8170+
const char *context)
8171+
{
8172+
char *end;
8173+
int major;
8174+
int minor;
8175+
ProtocolVersion version;
8176+
8177+
if (strcmp(value, "latest") == 0)
8178+
{
8179+
*result = PG_PROTOCOL_LATEST;
8180+
return true;
8181+
}
8182+
8183+
major = strtol(value, &end, 10);
8184+
if (*end != '.')
8185+
{
8186+
conn->status = CONNECTION_BAD;
8187+
libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
8188+
context,
8189+
value);
8190+
return false;
8191+
}
8192+
8193+
minor = strtol(&end[1], &end, 10);
8194+
if (*end != '\0')
8195+
{
8196+
conn->status = CONNECTION_BAD;
8197+
libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
8198+
context,
8199+
value);
8200+
return false;
8201+
}
8202+
8203+
version = PG_PROTOCOL(major, minor);
8204+
if (version > PG_PROTOCOL_LATEST ||
8205+
version < PG_PROTOCOL_EARLIEST)
8206+
{
8207+
conn->status = CONNECTION_BAD;
8208+
libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
8209+
context,
8210+
value);
8211+
return false;
8212+
}
8213+
8214+
*result = version;
8215+
return true;
8216+
}
8217+
81098218
/*
81108219
* To keep the API consistent, the locking stubs are always provided, even
81118220
* if they are not required.

src/interfaces/libpq/libpq-int.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ struct pg_conn
425425
char *gsslib; /* What GSS library to use ("gssapi" or
426426
* "sspi") */
427427
char *gssdelegation; /* Try to delegate GSS credentials? (0 or 1) */
428+
char *min_protocol_version; /* minimum used protocol version */
429+
char *max_protocol_version; /* maximum used protocol version */
428430
char *ssl_min_protocol_version; /* minimum TLS protocol version */
429431
char *ssl_max_protocol_version; /* maximum TLS protocol version */
430432
char *target_session_attrs; /* desired session properties */
@@ -534,6 +536,8 @@ struct pg_conn
534536
void *scram_client_key_binary; /* binary SCRAM client key */
535537
size_t scram_server_key_len;
536538
void *scram_server_key_binary; /* binary SCRAM server key */
539+
ProtocolVersion min_pversion; /* protocol version to request */
540+
ProtocolVersion max_pversion; /* protocol version to request */
537541

538542
/* Miscellaneous stuff */
539543
int be_pid; /* PID of backend --- needed for cancels */

0 commit comments

Comments
 (0)