Skip to content

Commit 864f80f

Browse files
committed
Fix handling of SCRAM-SHA-256's channel binding with RSA-PSS certificates
OpenSSL 1.1.1 and newer versions have added support for RSA-PSS certificates, which requires the use of a specific routine in OpenSSL to determine which hash function to use when compiling it when using channel binding in SCRAM-SHA-256. X509_get_signature_nid(), that is the original routine the channel binding code has relied on, is not able to determine which hash algorithm to use for such certificates. However, X509_get_signature_info(), new to OpenSSL 1.1.1, is able to do it. This commit switches the channel binding logic to rely on X509_get_signature_info() over X509_get_signature_nid(), which would be the choice when building with 1.1.1 or newer. The error could have been triggered on the client or the server, hence libpq and the backend need to have their related code paths patched. Note that attempting to load an RSA-PSS certificate with OpenSSL 1.1.0 or older leads to a failure due to an unsupported algorithm. The discovery of relying on X509_get_signature_info() comes from Jacob, the tests have been written by Heikki (with few tweaks from me), while I have bundled the whole together while adding the bits needed for MSVC and meson. This issue exists since channel binding exists, so backpatch all the way down. Some tests are added in 15~, triggered if compiling with OpenSSL 1.1.1 or newer, where the certificate and key files can easily be generated for RSA-PSS. Reported-by: Gunnar "Nick" Bluth Author: Jacob Champion, Heikki Linnakangas Discussion: https://postgr.es/m/17760-b6c61e752ec07060@postgresql.org Backpatch-through: 11
1 parent 4aa43ba commit 864f80f

File tree

8 files changed

+42
-7
lines changed

8 files changed

+42
-7
lines changed

configure

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13092,6 +13092,18 @@ if test "x$ac_cv_func_CRYPTO_lock" = xyes; then :
1309213092
#define HAVE_CRYPTO_LOCK 1
1309313093
_ACEOF
1309413094

13095+
fi
13096+
done
13097+
13098+
# Function introduced in OpenSSL 1.1.1.
13099+
for ac_func in X509_get_signature_info
13100+
do :
13101+
ac_fn_c_check_func "$LINENO" "X509_get_signature_info" "ac_cv_func_X509_get_signature_info"
13102+
if test "x$ac_cv_func_X509_get_signature_info" = xyes; then :
13103+
cat >>confdefs.h <<_ACEOF
13104+
#define HAVE_X509_GET_SIGNATURE_INFO 1
13105+
_ACEOF
13106+
1309513107
fi
1309613108
done
1309713109

configure.ac

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,8 @@ if test "$with_ssl" = openssl ; then
13161316
# thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
13171317
# function was removed.
13181318
AC_CHECK_FUNCS([CRYPTO_lock])
1319+
# Function introduced in OpenSSL 1.1.1.
1320+
AC_CHECK_FUNCS([X509_get_signature_info])
13191321
AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
13201322
elif test "$with_ssl" != no ; then
13211323
AC_MSG_ERROR([--with-ssl must specify openssl])

src/backend/libpq/be-secure-openssl.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,7 @@ be_tls_get_peer_serial(Port *port, char *ptr, size_t len)
13131313
ptr[0] = '\0';
13141314
}
13151315

1316-
#ifdef HAVE_X509_GET_SIGNATURE_NID
1316+
#if defined(HAVE_X509_GET_SIGNATURE_NID) || defined(HAVE_X509_GET_SIGNATURE_INFO)
13171317
char *
13181318
be_tls_get_certificate_hash(Port *port, size_t *len)
13191319
{
@@ -1331,10 +1331,15 @@ be_tls_get_certificate_hash(Port *port, size_t *len)
13311331

13321332
/*
13331333
* Get the signature algorithm of the certificate to determine the hash
1334-
* algorithm to use for the result.
1334+
* algorithm to use for the result. Prefer X509_get_signature_info(),
1335+
* introduced in OpenSSL 1.1.1, which can handle RSA-PSS signatures.
13351336
*/
1337+
#if HAVE_X509_GET_SIGNATURE_INFO
1338+
if (!X509_get_signature_info(server_cert, &algo_nid, NULL, NULL, NULL))
1339+
#else
13361340
if (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert),
13371341
&algo_nid, NULL))
1342+
#endif
13381343
elog(ERROR, "could not determine server certificate signature algorithm");
13391344

13401345
/*

src/include/libpq/libpq-be.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);
301301
* This is not supported with old versions of OpenSSL that don't have
302302
* the X509_get_signature_nid() function.
303303
*/
304-
#if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
304+
#if defined(USE_OPENSSL) && (defined(HAVE_X509_GET_SIGNATURE_NID) || defined(HAVE_X509_GET_SIGNATURE_INFO))
305305
#define HAVE_BE_TLS_GET_CERTIFICATE_HASH
306306
extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
307307
#endif

src/include/pg_config.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,9 @@
721721
/* Define to 1 if you have the `writev' function. */
722722
#undef HAVE_WRITEV
723723

724+
/* Define to 1 if you have the `X509_get_signature_info' function. */
725+
#undef HAVE_X509_GET_SIGNATURE_INFO
726+
724727
/* Define to 1 if you have the `X509_get_signature_nid' function. */
725728
#undef HAVE_X509_GET_SIGNATURE_NID
726729

src/interfaces/libpq/fe-secure-openssl.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
371371
return n;
372372
}
373373

374-
#ifdef HAVE_X509_GET_SIGNATURE_NID
374+
#if defined(HAVE_X509_GET_SIGNATURE_NID) || defined(HAVE_X509_GET_SIGNATURE_INFO)
375375
char *
376376
pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
377377
{
@@ -391,10 +391,15 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
391391

392392
/*
393393
* Get the signature algorithm of the certificate to determine the hash
394-
* algorithm to use for the result.
394+
* algorithm to use for the result. Prefer X509_get_signature_info(),
395+
* introduced in OpenSSL 1.1.1, which can handle RSA-PSS signatures.
395396
*/
397+
#if HAVE_X509_GET_SIGNATURE_INFO
398+
if (!X509_get_signature_info(peer_cert, &algo_nid, NULL, NULL, NULL))
399+
#else
396400
if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),
397401
&algo_nid, NULL))
402+
#endif
398403
{
399404
appendPQExpBufferStr(&conn->errorMessage,
400405
libpq_gettext("could not determine server certificate signature algorithm\n"));

src/interfaces/libpq/libpq-int.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
787787
* This is not supported with old versions of OpenSSL that don't have
788788
* the X509_get_signature_nid() function.
789789
*/
790-
#if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
790+
#if defined(USE_OPENSSL) && (defined(HAVE_X509_GET_SIGNATURE_NID) || defined(HAVE_X509_GET_SIGNATURE_INFO))
791791
#define HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
792792
extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len);
793793
#endif

src/tools/msvc/Solution.pm

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ sub GenerateFiles
434434
HAVE_WCTYPE_H => 1,
435435
HAVE_WRITEV => undef,
436436
HAVE_X509_GET_SIGNATURE_NID => 1,
437+
HAVE_X509_GET_SIGNATURE_INFO => undef,
437438
HAVE_X86_64_POPCNTQ => undef,
438439
HAVE__BOOL => undef,
439440
HAVE__BUILTIN_BSWAP16 => undef,
@@ -549,7 +550,14 @@ sub GenerateFiles
549550

550551
my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion();
551552

552-
# More symbols are needed with OpenSSL 1.1.0 and above.
553+
# Symbols needed with OpenSSL 1.1.1 and above.
554+
if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
555+
|| ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '1'))
556+
{
557+
$define{HAVE_X509_GET_SIGNATURE_INFO} = 1;
558+
}
559+
560+
# Symbols needed with OpenSSL 1.1.0 and above.
553561
if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
554562
|| ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0'))
555563
{

0 commit comments

Comments
 (0)