diff options
author | Michael Paquier <michael@paquier.xyz> | 2020-01-28 10:40:48 +0900 |
---|---|---|
committer | Michael Paquier <michael@paquier.xyz> | 2020-01-28 10:40:48 +0900 |
commit | ff8ca5fadd819155c82bd16fcc6b7231af649cf8 (patch) | |
tree | 1e68ff107bb0607f796078c47f333be146fdffe4 /src/interfaces/libpq/fe-connect.c | |
parent | 6f38d4dac381b5b8bead302a0b4f81761042cd25 (diff) | |
download | postgresql-ff8ca5fadd819155c82bd16fcc6b7231af649cf8.tar.gz postgresql-ff8ca5fadd819155c82bd16fcc6b7231af649cf8.zip |
Add connection parameters to control SSL protocol min/max in libpq
These two new parameters, named sslminprotocolversion and
sslmaxprotocolversion, allow to respectively control the minimum and the
maximum version of the SSL protocol used for the SSL connection attempt.
The default setting is to allow any version for both the minimum and the
maximum bounds, causing libpq to rely on the bounds set by the backend
when negotiating the protocol to use for an SSL connection. The bounds
are checked when the values are set at the earliest stage possible as
this makes the checks independent of any SSL implementation.
Author: Daniel Gustafsson
Reviewed-by: Michael Paquier, Cary Huang
Discussion: https://postgr.es/m/4F246AE3-A7AE-471E-BD3D-C799D3748E03@yesql.se
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 80b54bc92b2..8498f32f8d4 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -320,6 +320,14 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Require-Peer", "", 10, offsetof(struct pg_conn, requirepeer)}, + {"sslminprotocolversion", "PGSSLMINPROTOCOLVERSION", NULL, NULL, + "SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */ + offsetof(struct pg_conn, sslminprotocolversion)}, + + {"sslmaxprotocolversion", "PGSSLMAXPROTOCOLVERSION", NULL, NULL, + "SSL-Maximum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */ + offsetof(struct pg_conn, sslmaxprotocolversion)}, + /* * As with SSL, all GSS options are exposed even in builds that don't have * support. @@ -426,6 +434,8 @@ static char *passwordFromFile(const char *hostname, const char *port, const char const char *username, const char *pgpassfile); static void pgpassfileWarning(PGconn *conn); static void default_threadlock(int acquire); +static bool sslVerifyProtocolVersion(const char *version); +static bool sslVerifyProtocolRange(const char *min, const char *max); /* global variable because fe-auth.c needs to access it */ @@ -1286,6 +1296,40 @@ connectOptions2(PGconn *conn) } /* + * Validate TLS protocol versions for sslminprotocolversion and + * sslmaxprotocolversion. + */ + if (!sslVerifyProtocolVersion(conn->sslminprotocolversion)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid sslminprotocolversion value: \"%s\"\n"), + conn->sslminprotocolversion); + return false; + } + if (!sslVerifyProtocolVersion(conn->sslmaxprotocolversion)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid sslmaxprotocolversion value: \"%s\"\n"), + conn->sslmaxprotocolversion); + return false; + } + + /* + * Check if the range of SSL protocols defined is correct. This is done + * at this early step because this is independent of the SSL + * implementation used, and this avoids unnecessary cycles with an + * already-built SSL context when the connection is being established, as + * it would be doomed anyway. + */ + if (!sslVerifyProtocolRange(conn->sslminprotocolversion, + conn->sslmaxprotocolversion)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid SSL protocol version range")); + return false; + } + + /* * validate gssencmode option */ if (conn->gssencmode) @@ -4001,6 +4045,10 @@ freePGconn(PGconn *conn) free(conn->sslcompression); if (conn->requirepeer) free(conn->requirepeer); + if (conn->sslminprotocolversion) + free(conn->sslminprotocolversion); + if (conn->sslmaxprotocolversion) + free(conn->sslmaxprotocolversion); if (conn->gssencmode) free(conn->gssencmode); if (conn->krbsrvname) @@ -7031,6 +7079,71 @@ pgpassfileWarning(PGconn *conn) } } +/* + * Check if the SSL procotol value given in input is valid or not. + * This is used as a sanity check routine for the connection parameters + * sslminprotocolversion and sslmaxprotocolversion. + */ +static bool +sslVerifyProtocolVersion(const char *version) +{ + /* + * An empty string and a NULL value are considered valid as it is + * equivalent to ignoring the parameter. + */ + if (!version || strlen(version) == 0) + return true; + + if (pg_strcasecmp(version, "TLSv1") == 0 || + pg_strcasecmp(version, "TLSv1.1") == 0 || + pg_strcasecmp(version, "TLSv1.2") == 0 || + pg_strcasecmp(version, "TLSv1.3") == 0) + return true; + + /* anything else is wrong */ + return false; +} + + +/* + * Ensure that the SSL protocol range given in input is correct. The check + * is performed on the input string to keep it TLS backend agnostic. Input + * to this function is expected verified with sslVerifyProtocolVersion(). + */ +static bool +sslVerifyProtocolRange(const char *min, const char *max) +{ + Assert(sslVerifyProtocolVersion(min) && + sslVerifyProtocolVersion(max)); + + /* If at least one of the bounds is not set, the range is valid */ + if (min == NULL || max == NULL || strlen(min) == 0 || strlen(max) == 0) + return true; + + /* + * If the minimum version is the lowest one we accept, then all options + * for the maximum are valid. + */ + if (pg_strcasecmp(min, "TLSv1") == 0) + return true; + + /* + * The minimum bound is valid, and cannot be TLSv1, so using TLSv1 for the + * maximum is incorrect. + */ + if (pg_strcasecmp(max, "TLSv1") == 0) + return false; + + /* + * At this point we know that we have a mix of TLSv1.1 through 1.3 + * versions. + */ + if (pg_strcasecmp(min, max) > 0) + return false; + + return true; +} + /* * Obtain user's home directory, return in given buffer |