postgres.git / summary / log / commit / refs
commit d93ef413174daae721c6f2cfda3fbd5187f0b4ee
Author: Michael Paquier <michael@paquier.xyz>
Commit: Noah Misch <noah@leadboat.com>
Date: Mon May 11 12:13:47 2026 +0000
First released in: 14.23, 15.18, 16.14, 17.10, 18.4
Apply timingsafe_bcmp() in authentication paths
This commit applies timingsafe_bcmp() to authentication paths that
handle attributes or data previously compared with memcpy() or strcmp(),
which are sensitive to timing attacks.
The following data is concerned by this change, some being in the
backend and some in the frontend:
- For a SCRAM or MD5 password, the computed key or the MD5 hash compared
with a password during a plain authentication.
- For a SCRAM exchange, the stored key, the client's final nonce and the
server nonce.
- RADIUS (up to v18), the encrypted password.
- For MD5 authentication, the MD5(MD5()) hash.
Reported-by: Joe Conway <mail@joeconway.com>
Security: CVE-2026-6478
Author: Michael Paquier <michael@paquier.xyz>
Reviewed-by: John Naylor <johncnaylorls@gmail.com>
Backpatch-through: 14
src/backend/libpq/auth-scram.c | 8 ++++----
src/backend/libpq/auth.c | 2 +-
src/backend/libpq/crypt.c | 6 ++++--
src/interfaces/libpq/fe-auth-scram.c | 6 +++---
4 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index db778405724..8fe5b989c2f 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -581,7 +581,7 @@ scram_verify_plain_password(const char *username, const char *password,
* Compare the secret's Server Key with the one computed from the
* user-supplied password.
*/
- return memcmp(computed_key, server_key, key_length) == 0;
+ return timingsafe_bcmp(computed_key, server_key, key_length) == 0;
}
@@ -1132,9 +1132,9 @@ verify_final_nonce(scram_state *state)
if (final_nonce_len != client_nonce_len + server_nonce_len)
return false;
- if (memcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
+ if (timingsafe_bcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
return false;
- if (memcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
+ if (timingsafe_bcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
return false;
return true;
@@ -1188,7 +1188,7 @@ verify_client_proof(scram_state *state)
client_StoredKey, &errstr) < 0)
elog(ERROR, "could not hash stored key: %s", errstr);
- if (memcmp(client_StoredKey, state->StoredKey, state->key_length) != 0)
+ if (timingsafe_bcmp(client_StoredKey, state->StoredKey, state->key_length) != 0)
return false;
return true;
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 4da46666439..40b6f93bffc 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -3232,7 +3232,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
}
pfree(cryptvector);
- if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
+ if (timingsafe_bcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
{
ereport(LOG,
(errmsg("RADIUS response from %s has incorrect MD5 signature",
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index f6b641e726e..063ac95778d 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -230,7 +230,8 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
return STATUS_ERROR;
}
- if (strcmp(client_pass, crypt_pwd) == 0)
+ if (strlen(client_pass) == strlen(crypt_pwd) &&
+ timingsafe_bcmp(client_pass, crypt_pwd, strlen(crypt_pwd)) == 0)
retval = STATUS_OK;
else
{
@@ -292,7 +293,8 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
*logdetail = errstr;
return STATUS_ERROR;
}
- if (strcmp(crypt_client_pass, shadow_pass) == 0)
+ if (strlen(crypt_client_pass) == strlen(shadow_pass) &&
+ timingsafe_bcmp(crypt_client_pass, shadow_pass, strlen(shadow_pass)) == 0)
return STATUS_OK;
else
{
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index f6d6a5aa977..f0feeeaca51 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -631,7 +631,7 @@ read_server_first_message(fe_scram_state *state, char *input)
/* Verify immediately that the server used our part of the nonce */
if (strlen(nonce) < strlen(state->client_nonce) ||
- memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
+ timingsafe_bcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
{
libpq_append_conn_error(conn, "invalid SCRAM response (nonce mismatch)");
return false;
@@ -896,8 +896,8 @@ verify_server_signature(fe_scram_state *state, bool *match,
pg_hmac_free(ctx);
/* signature processed, so now check after it */
- if (memcmp(expected_ServerSignature, state->ServerSignature,
- state->key_length) != 0)
+ if (timingsafe_bcmp(expected_ServerSignature, state->ServerSignature,
+ state->key_length) != 0)
*match = false;
else
*match = true;
[parent: c5790ec4fd9a]