0001-WIP-SCRAM-SHA-512.patch
text/x-diff
Filename: 0001-WIP-SCRAM-SHA-512.patch
Type: text/x-diff
Part: 0
Patch
Format: format-patch
Series: patch 0001
Subject: WIP SCRAM-SHA-512
| File | + | − |
|---|---|---|
| src/backend/libpq/auth.c | 9 | 6 |
| src/backend/libpq/auth-scram.c | 116 | 27 |
| src/backend/libpq/crypt.c | 11 | 2 |
| src/backend/libpq/hba.c | 4 | 1 |
| src/backend/utils/misc/guc_tables.c | 1 | 0 |
| src/common/scram-common.c | 10 | 3 |
| src/include/common/scram-common.h | 4 | 1 |
| src/include/libpq/crypt.h | 2 | 1 |
| src/include/libpq/hba.h | 2 | 1 |
| src/include/libpq/scram.h | 2 | 1 |
| src/interfaces/libpq/fe-auth.c | 34 | 18 |
| src/interfaces/libpq/fe-auth.h | 2 | 0 |
| src/interfaces/libpq/fe-auth-scram.c | 19 | 5 |
| src/test/authentication/t/001_password.pl | 20 | 1 |
From 49a3c993a1df722da01f16839eb1d6185c205092 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 20 Dec 2022 16:14:41 +0900
Subject: [PATCH] WIP SCRAM-SHA-512
---
src/include/common/scram-common.h | 5 +-
src/include/libpq/crypt.h | 3 +-
src/include/libpq/hba.h | 3 +-
src/include/libpq/scram.h | 3 +-
src/backend/libpq/auth-scram.c | 143 ++++++++++++++++++----
src/backend/libpq/auth.c | 15 ++-
src/backend/libpq/crypt.c | 13 +-
src/backend/libpq/hba.c | 5 +-
src/backend/utils/misc/guc_tables.c | 1 +
src/common/scram-common.c | 13 +-
src/interfaces/libpq/fe-auth-scram.c | 24 +++-
src/interfaces/libpq/fe-auth.c | 52 +++++---
src/interfaces/libpq/fe-auth.h | 2 +
src/test/authentication/t/001_password.pl | 21 +++-
14 files changed, 236 insertions(+), 67 deletions(-)
diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h
index 953d30ac54..10996fa735 100644
--- a/src/include/common/scram-common.h
+++ b/src/include/common/scram-common.h
@@ -19,15 +19,18 @@
/* Name of SCRAM mechanisms per IANA */
#define SCRAM_SHA_256_NAME "SCRAM-SHA-256"
#define SCRAM_SHA_256_PLUS_NAME "SCRAM-SHA-256-PLUS" /* with channel binding */
+#define SCRAM_SHA_512_NAME "SCRAM-SHA-512"
+#define SCRAM_SHA_512_PLUS_NAME "SCRAM-SHA-512-PLUS" /* with channel binding */
/* Length of SCRAM keys (client and server) */
#define SCRAM_SHA_256_KEY_LEN PG_SHA256_DIGEST_LENGTH
+#define SCRAM_SHA_512_KEY_LEN PG_SHA512_DIGEST_LENGTH
/*
* Size of buffers used internally by SCRAM routines, that should be the
* maximum of SCRAM_SHA_*_KEY_LEN among the hash methods supported.
*/
-#define SCRAM_MAX_KEY_LEN SCRAM_SHA_256_KEY_LEN
+#define SCRAM_MAX_KEY_LEN SCRAM_SHA_512_KEY_LEN
/*
* Size of random nonce generated in the authentication exchange. This
diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h
index 3238cf66d3..992d4922fc 100644
--- a/src/include/libpq/crypt.h
+++ b/src/include/libpq/crypt.h
@@ -28,7 +28,8 @@ typedef enum PasswordType
{
PASSWORD_TYPE_PLAINTEXT = 0,
PASSWORD_TYPE_MD5,
- PASSWORD_TYPE_SCRAM_SHA_256
+ PASSWORD_TYPE_SCRAM_SHA_256,
+ PASSWORD_TYPE_SCRAM_SHA_512
} PasswordType;
extern PasswordType get_password_type(const char *shadow_pass);
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 90c51ad6fa..dc536225ef 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -30,7 +30,8 @@ typedef enum UserAuth
uaIdent,
uaPassword,
uaMD5,
- uaSCRAM,
+ uaSCRAM256,
+ uaSCRAM512,
uaGSS,
uaSSPI,
uaPAM,
diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h
index b29501ef96..f2aa5c82c2 100644
--- a/src/include/libpq/scram.h
+++ b/src/include/libpq/scram.h
@@ -22,7 +22,8 @@
extern PGDLLIMPORT const pg_be_sasl_mech pg_be_scram_mech;
/* Routines to handle and check SCRAM-SHA-256 secret */
-extern char *pg_be_scram_build_secret(const char *password);
+extern char *pg_be_scram_build_secret(const char *password,
+ pg_cryptohash_type hash_type);
extern bool parse_scram_secret(const char *secret,
int *iterations,
pg_cryptohash_type *hash_type,
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 126eb70974..01cf5da754 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -181,7 +181,9 @@ static char *build_server_first_message(scram_state *state);
static char *build_server_final_message(scram_state *state);
static bool verify_client_proof(scram_state *state);
static bool verify_final_nonce(scram_state *state);
-static void mock_scram_secret(const char *username, pg_cryptohash_type *hash_type,
+static void mock_scram_secret(const char *username,
+ pg_cryptohash_type mock_hash,
+ pg_cryptohash_type *hash_type,
int *iterations, int *key_length, char **salt,
uint8 *stored_key, uint8 *server_key);
static bool is_scram_printable(char *p);
@@ -210,12 +212,28 @@ scram_get_mechanisms(Port *port, StringInfo buf)
#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH
if (port->ssl_in_use)
{
- appendStringInfoString(buf, SCRAM_SHA_256_PLUS_NAME);
- appendStringInfoChar(buf, '\0');
+ if (port->hba->auth_method == uaSCRAM512)
+ {
+ appendStringInfoString(buf, SCRAM_SHA_512_PLUS_NAME);
+ appendStringInfoChar(buf, '\0');
+ }
+ else
+ {
+ appendStringInfoString(buf, SCRAM_SHA_256_PLUS_NAME);
+ appendStringInfoChar(buf, '\0');
+ }
}
#endif
- appendStringInfoString(buf, SCRAM_SHA_256_NAME);
- appendStringInfoChar(buf, '\0');
+ if (port->hba->auth_method == uaSCRAM512)
+ {
+ appendStringInfoString(buf, SCRAM_SHA_512_NAME);
+ appendStringInfoChar(buf, '\0');
+ }
+ else
+ {
+ appendStringInfoString(buf, SCRAM_SHA_256_NAME);
+ appendStringInfoChar(buf, '\0');
+ }
}
/*
@@ -238,11 +256,14 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
{
scram_state *state;
bool got_secret;
+ pg_cryptohash_type mock_hash;
state = (scram_state *) palloc0(sizeof(scram_state));
state->port = port;
state->state = SCRAM_AUTH_INIT;
+ mock_hash = PG_SHA256;
+
/*
* Parse the selected mechanism.
*
@@ -253,17 +274,42 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
* selecting any other SASL mechanism we don't support.
*/
#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH
- if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && port->ssl_in_use)
+ if ((strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 ||
+ strcmp(selected_mech, SCRAM_SHA_512_PLUS_NAME) == 0) &&
+ port->ssl_in_use)
state->channel_binding_in_use = true;
else
#endif
- if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0)
+ if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0 ||
+ strcmp(selected_mech, SCRAM_SHA_512_NAME) == 0)
state->channel_binding_in_use = false;
else
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("client selected an invalid SASL authentication mechanism")));
+ /*
+ * If the HBA entry does not match with the selected mechanism, the
+ * authentication should fail, so go through the mock process in this
+ * case.
+ */
+ if (port->hba->auth_method == uaSCRAM256 &&
+ strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) != 0 &&
+ strcmp(selected_mech, SCRAM_SHA_256_NAME) != 0)
+ {
+ /* the client has selected one of the SHA-512 mechanisms */
+ shadow_pass = NULL;
+ mock_hash = PG_SHA512;
+ }
+ if (port->hba->auth_method == uaSCRAM512 &&
+ strcmp(selected_mech, SCRAM_SHA_512_PLUS_NAME) != 0 &&
+ strcmp(selected_mech, SCRAM_SHA_512_NAME) != 0)
+ {
+ /* the client has selected one of the SHA-256 mechanisms */
+ shadow_pass = NULL;
+ mock_hash = PG_SHA256;
+ }
+
/*
* Parse the stored secret.
*/
@@ -271,15 +317,37 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
{
int password_type = get_password_type(shadow_pass);
- if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
+ if (password_type == PASSWORD_TYPE_SCRAM_SHA_256 ||
+ password_type == PASSWORD_TYPE_SCRAM_SHA_512)
{
- if (parse_scram_secret(shadow_pass, &state->iterations,
- &state->hash_type, &state->key_length,
- &state->salt,
- state->StoredKey,
- state->ServerKey))
- got_secret = true;
- else
+ got_secret = parse_scram_secret(shadow_pass,
+ &state->iterations,
+ &state->hash_type,
+ &state->key_length,
+ &state->salt,
+ state->StoredKey,
+ state->ServerKey);
+
+ /*
+ * At this point, we already know that the HBA entry maps
+ * with a matching mechanism. If the password type does not
+ * not match any of that, do a mock processing using the
+ * hash selected by the client's mechanism.
+ */
+ if (port->hba->auth_method == uaSCRAM256 &&
+ password_type != PASSWORD_TYPE_SCRAM_SHA_256)
+ {
+ mock_hash = PG_SHA256;
+ got_secret = false;
+ }
+ else if (port->hba->auth_method == uaSCRAM512 &&
+ password_type != PASSWORD_TYPE_SCRAM_SHA_512)
+ {
+ mock_hash = PG_SHA512;
+ got_secret = false;
+ }
+
+ if (!got_secret)
{
/*
* The password looked like a SCRAM secret, but could not be
@@ -288,7 +356,6 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
ereport(LOG,
(errmsg("invalid SCRAM secret for user \"%s\"",
state->port->user_name)));
- got_secret = false;
}
}
else
@@ -309,6 +376,7 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
* considered normal, since the caller requested it, so don't set log
* detail.
*/
+ mock_hash = PG_SHA256;
got_secret = false;
}
@@ -320,7 +388,8 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
*/
if (!got_secret)
{
- mock_scram_secret(state->port->user_name, &state->hash_type,
+ mock_scram_secret(state->port->user_name, mock_hash,
+ &state->hash_type,
&state->iterations, &state->key_length,
&state->salt,
state->StoredKey, state->ServerKey);
@@ -471,7 +540,7 @@ scram_exchange(void *opaq, const char *input, int inputlen,
* The result is palloc'd, so caller is responsible for freeing it.
*/
char *
-pg_be_scram_build_secret(const char *password)
+pg_be_scram_build_secret(const char *password, pg_cryptohash_type hash_type)
{
char *prep_password;
pg_saslprep_rc rc;
@@ -494,7 +563,9 @@ pg_be_scram_build_secret(const char *password)
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate random salt")));
- result = scram_build_secret(PG_SHA256, SCRAM_SHA_256_KEY_LEN,
+ result = scram_build_secret(hash_type,
+ (hash_type == PG_SHA512) ?
+ SCRAM_SHA_512_KEY_LEN : SCRAM_SHA_256_KEY_LEN,
saltbuf, SCRAM_DEFAULT_SALT_LEN,
SCRAM_DEFAULT_ITERATIONS, password,
&errstr);
@@ -622,10 +693,18 @@ parse_scram_secret(const char *secret, int *iterations,
goto invalid_secret;
/* Parse the fields */
- if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
+ if (strcmp(scheme_str, "SCRAM-SHA-256") == 0)
+ {
+ *hash_type = PG_SHA256;
+ *key_length = SCRAM_SHA_256_KEY_LEN;
+ }
+ else if (strcmp(scheme_str, "SCRAM-SHA-512") == 0)
+ {
+ *hash_type = PG_SHA512;
+ *key_length = SCRAM_SHA_512_KEY_LEN;
+ }
+ else
goto invalid_secret;
- *hash_type = PG_SHA256;
- *key_length = SCRAM_SHA_256_KEY_LEN;
errno = 0;
*iterations = strtol(iterations_str, &p, 10);
@@ -675,14 +754,16 @@ invalid_secret:
*
* In a normal authentication, these are extracted from the secret
* stored in the server. This function generates values that look
- * realistic, for when there is no stored secret, using SCRAM-SHA-256.
+ * realistic, for when there is no stored secret, using the hash method
+ * specified by mock_hash.
*
* Like in parse_scram_secret(), for 'stored_key' and 'server_key', the
* caller must pass pre-allocated buffers of size SCRAM_MAX_KEY_LEN, and
* the buffer for the salt is palloc'd by this function.
*/
static void
-mock_scram_secret(const char *username, pg_cryptohash_type *hash_type,
+mock_scram_secret(const char *username, pg_cryptohash_type mock_hash,
+ pg_cryptohash_type *hash_type,
int *iterations, int *key_length, char **salt,
uint8 *stored_key, uint8 *server_key)
{
@@ -691,8 +772,16 @@ mock_scram_secret(const char *username, pg_cryptohash_type *hash_type,
int encoded_len;
/* Enforce the use of SHA-256, which would be realistic enough */
- *hash_type = PG_SHA256;
- *key_length = SCRAM_SHA_256_KEY_LEN;
+ if (mock_hash == PG_SHA256)
+ {
+ *hash_type = PG_SHA256;
+ *key_length = SCRAM_SHA_256_KEY_LEN;
+ }
+ else
+ {
+ *hash_type = PG_SHA512;
+ *key_length = SCRAM_SHA_512_KEY_LEN;
+ }
/*
* Generate deterministic salt.
@@ -1477,7 +1566,7 @@ scram_mock_salt(const char *username, pg_cryptohash_type hash_type,
* This may be worth refreshing if support for more hash methods is\
* added.
*/
- Assert(hash_type == PG_SHA256);
+ Assert(hash_type == PG_SHA256 || hash_type == PG_SHA512);
ctx = pg_cryptohash_create(hash_type);
if (pg_cryptohash_init(ctx) < 0 ||
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index e2f723d188..6109953e8b 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -276,7 +276,8 @@ auth_failed(Port *port, int status, const char *logdetail)
break;
case uaPassword:
case uaMD5:
- case uaSCRAM:
+ case uaSCRAM256:
+ case uaSCRAM512:
errstr = gettext_noop("password authentication failed for user \"%s\"");
/* We use it to indicate if a .pgpass password failed. */
errcode_return = ERRCODE_INVALID_PASSWORD;
@@ -585,7 +586,8 @@ ClientAuthentication(Port *port)
break;
case uaMD5:
- case uaSCRAM:
+ case uaSCRAM256:
+ case uaSCRAM512:
status = CheckPWChallengeAuth(port, &logdetail);
break;
@@ -806,7 +808,8 @@ CheckPWChallengeAuth(Port *port, const char **logdetail)
char *shadow_pass;
PasswordType pwtype;
- Assert(port->hba->auth_method == uaSCRAM ||
+ Assert(port->hba->auth_method == uaSCRAM256 ||
+ port->hba->auth_method == uaSCRAM512 ||
port->hba->auth_method == uaMD5);
/* First look up the user's password. */
@@ -828,9 +831,9 @@ CheckPWChallengeAuth(Port *port, const char **logdetail)
/*
* If 'md5' authentication is allowed, decide whether to perform 'md5' or
- * 'scram-sha-256' authentication based on the type of password the user
- * has. If it's an MD5 hash, we must do MD5 authentication, and if it's a
- * SCRAM secret, we must do SCRAM authentication.
+ * 'scram-sha-{256,512}' authentication based on the type of password the
+ * user has. If it's an MD5 hash, we must do MD5 authentication, and if
+ * it's a SCRAM secret, we must do SCRAM authentication.
*
* If MD5 authentication is not allowed, always use SCRAM. If the user
* had an MD5 password, CheckSASLAuth() with the SCRAM mechanism will
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index a81af0749a..3c0f49cb05 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -101,7 +101,12 @@ get_password_type(const char *shadow_pass)
return PASSWORD_TYPE_MD5;
if (parse_scram_secret(shadow_pass, &iterations, &hash_type, &key_length,
&encoded_salt, stored_key, server_key))
- return PASSWORD_TYPE_SCRAM_SHA_256;
+ {
+ if (hash_type == PG_SHA512)
+ return PASSWORD_TYPE_SCRAM_SHA_512;
+ else
+ return PASSWORD_TYPE_SCRAM_SHA_256;
+ }
return PASSWORD_TYPE_PLAINTEXT;
}
@@ -140,7 +145,10 @@ encrypt_password(PasswordType target_type, const char *role,
return encrypted_password;
case PASSWORD_TYPE_SCRAM_SHA_256:
- return pg_be_scram_build_secret(password);
+ return pg_be_scram_build_secret(password, PG_SHA256);
+
+ case PASSWORD_TYPE_SCRAM_SHA_512:
+ return pg_be_scram_build_secret(password, PG_SHA512);
case PASSWORD_TYPE_PLAINTEXT:
elog(ERROR, "cannot encrypt password with 'plaintext'");
@@ -235,6 +243,7 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
switch (get_password_type(shadow_pass))
{
case PASSWORD_TYPE_SCRAM_SHA_256:
+ case PASSWORD_TYPE_SCRAM_SHA_512:
if (scram_verify_plain_password(role,
client_pass,
shadow_pass))
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 870b907697..53fd31de72 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -117,6 +117,7 @@ static const char *const UserAuthName[] =
"password",
"md5",
"scram-sha-256",
+ "scram-sha-512",
"gss",
"sspi",
"pam",
@@ -1776,7 +1777,9 @@ parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
parsedline->auth_method = uaMD5;
}
else if (strcmp(token->string, "scram-sha-256") == 0)
- parsedline->auth_method = uaSCRAM;
+ parsedline->auth_method = uaSCRAM256;
+ else if (strcmp(token->string, "scram-sha-512") == 0)
+ parsedline->auth_method = uaSCRAM512;
else if (strcmp(token->string, "pam") == 0)
#ifdef USE_PAM
parsedline->auth_method = uaPAM;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1bf14eec66..564173407a 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -383,6 +383,7 @@ static const struct config_enum_entry plan_cache_mode_options[] = {
static const struct config_enum_entry password_encryption_options[] = {
{"md5", PASSWORD_TYPE_MD5, false},
{"scram-sha-256", PASSWORD_TYPE_SCRAM_SHA_256, false},
+ {"scram-sha-512", PASSWORD_TYPE_SCRAM_SHA_512, false},
{NULL, 0, false}
};
diff --git a/src/common/scram-common.c b/src/common/scram-common.c
index bffbbb4317..d06669f2ee 100644
--- a/src/common/scram-common.c
+++ b/src/common/scram-common.c
@@ -203,6 +203,7 @@ scram_build_secret(pg_cryptohash_type hash_type, int key_length,
uint8 salted_password[SCRAM_MAX_KEY_LEN];
uint8 stored_key[SCRAM_MAX_KEY_LEN];
uint8 server_key[SCRAM_MAX_KEY_LEN];
+ char *header;
char *result;
char *p;
int maxlen;
@@ -212,7 +213,8 @@ scram_build_secret(pg_cryptohash_type hash_type, int key_length,
int encoded_result;
/* Only this hash method is supported currently */
- Assert(hash_type == PG_SHA256);
+ Assert(hash_type == PG_SHA256 ||
+ hash_type == PG_SHA512);
if (iterations <= 0)
iterations = SCRAM_DEFAULT_ITERATIONS;
@@ -246,7 +248,12 @@ scram_build_secret(pg_cryptohash_type hash_type, int key_length,
encoded_stored_len = pg_b64_enc_len(key_length);
encoded_server_len = pg_b64_enc_len(key_length);
- maxlen = strlen("SCRAM-SHA-256") + 1
+ if (hash_type == PG_SHA256)
+ header = "SCRAM-SHA-256";
+ else
+ header = "SCRAM-SHA-512";
+
+ maxlen = strlen(header) + 1
+ 10 + 1 /* iteration count */
+ encoded_salt_len + 1 /* Base64-encoded salt */
+ encoded_stored_len + 1 /* Base64-encoded StoredKey */
@@ -263,7 +270,7 @@ scram_build_secret(pg_cryptohash_type hash_type, int key_length,
result = palloc(maxlen);
#endif
- p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
+ p = result + sprintf(result, "%s$%d:", header, iterations);
/* salt */
encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index 7410d5ba52..1f6c47a942 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -110,8 +110,17 @@ scram_init(PGconn *conn,
memset(state, 0, sizeof(fe_scram_state));
state->conn = conn;
state->state = FE_SCRAM_INIT;
- state->key_length = SCRAM_SHA_256_KEY_LEN;
- state->hash_type = PG_SHA256;
+ if (strcmp(sasl_mechanism, SCRAM_SHA_512_NAME) == 0 ||
+ strcmp(sasl_mechanism, SCRAM_SHA_512_PLUS_NAME) == 0)
+ {
+ state->key_length = SCRAM_SHA_512_KEY_LEN;
+ state->hash_type = PG_SHA512;
+ }
+ else
+ {
+ state->key_length = SCRAM_SHA_256_KEY_LEN;
+ state->hash_type = PG_SHA256;
+ }
state->sasl_mechanism = strdup(sasl_mechanism);
if (!state->sasl_mechanism)
@@ -165,7 +174,8 @@ scram_channel_bound(void *opaq)
return false;
/* channel binding mechanism not used */
- if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
+ if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0 &&
+ strcmp(state->sasl_mechanism, SCRAM_SHA_512_PLUS_NAME) != 0)
return false;
/* all clear! */
@@ -894,7 +904,8 @@ verify_server_signature(fe_scram_state *state, bool *match,
* error details.
*/
char *
-pg_fe_scram_build_secret(const char *password, const char **errstr)
+pg_fe_scram_build_secret(const char *password, pg_cryptohash_type hash_type,
+ const char **errstr)
{
char *prep_password;
pg_saslprep_rc rc;
@@ -924,7 +935,10 @@ pg_fe_scram_build_secret(const char *password, const char **errstr)
return NULL;
}
- result = scram_build_secret(PG_SHA256, SCRAM_SHA_256_KEY_LEN, saltbuf,
+ result = scram_build_secret(hash_type,
+ (hash_type == PG_SHA512) ?
+ SCRAM_SHA_512_KEY_LEN : SCRAM_SHA_256_KEY_LEN,
+ saltbuf,
SCRAM_DEFAULT_SALT_LEN,
SCRAM_DEFAULT_ITERATIONS, password,
errstr);
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 4a6c358bb6..6b17ebcf9d 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -431,8 +431,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)
/*
* Parse the list of SASL authentication mechanisms in the
* AuthenticationSASL message, and select the best mechanism that we
- * support. SCRAM-SHA-256-PLUS and SCRAM-SHA-256 are the only ones
- * supported at the moment, listed by order of decreasing importance.
+ * support. SCRAM-SHA-256[-PLUS] and SCRAM-SHA-512[-PLUS] are the only
+ * ones supported at the moment, listed by order of decreasing importance.
*/
selected_mechanism = NULL;
for (;;)
@@ -451,17 +451,19 @@ pg_SASL_init(PGconn *conn, int payloadlen)
break;
/*
- * Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything
- * else if a channel binding type is set and if the client supports it
- * (and did not set channel_binding=disable). Pick SCRAM-SHA-256 if
- * nothing else has already been picked. If we add more mechanisms, a
- * more refined priority mechanism might become necessary.
+ * Select the mechanism to use. Pick SCRAM-SHA-{256,512}-PLUS over
+ * anything else if a channel binding type is set and if the client
+ * supports it (and did not set channel_binding=disable). Pick
+ * SCRAM-SHA-{256,512} if nothing else has already been picked. If
+ * we add more mechanisms, a more refined priority mechanism might
+ * become necessary.
*/
- if (strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0)
+ if (strcmp(mechanism_buf.data, SCRAM_SHA_512_PLUS_NAME) == 0 ||
+ strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0)
{
if (conn->ssl_in_use)
{
- /* The server has offered SCRAM-SHA-256-PLUS. */
+ /* The server has offered SCRAM-SHA-{256,512}-PLUS. */
#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
/*
@@ -470,7 +472,10 @@ pg_SASL_init(PGconn *conn, int payloadlen)
*/
if (conn->channel_binding[0] != 'd') /* disable */
{
- selected_mechanism = SCRAM_SHA_256_PLUS_NAME;
+ if (strcmp(mechanism_buf.data, SCRAM_SHA_512_PLUS_NAME) == 0)
+ selected_mechanism = SCRAM_SHA_512_PLUS_NAME;
+ else
+ selected_mechanism = SCRAM_SHA_256_PLUS_NAME;
conn->sasl = &pg_scram_mech;
}
#else
@@ -478,7 +483,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
* The client does not support channel binding. If it is
* required, complain immediately instead of the error below
* which would be confusing as the server is publishing
- * SCRAM-SHA-256-PLUS.
+ * SCRAM-SHA-{256,512}-PLUS.
*/
if (conn->channel_binding[0] == 'r') /* require */
{
@@ -490,18 +495,25 @@ pg_SASL_init(PGconn *conn, int payloadlen)
else
{
/*
- * The server offered SCRAM-SHA-256-PLUS, but the connection
- * is not SSL-encrypted. That's not sane. Perhaps SSL was
- * stripped by a proxy? There's no point in continuing,
+ * The server offered SCRAM-SHA-{256,512}-PLUS, but the
+ * connection is not SSL-encrypted. That's not sane. Perhaps
+ * SSL was stripped by a proxy? There's no point in continuing,
* because the server will reject the connection anyway if we
* try authenticate without channel binding even though both
* the client and server supported it. The SCRAM exchange
* checks for that, to prevent downgrade attacks.
*/
- libpq_append_conn_error(conn, "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection");
+ libpq_append_conn_error(conn, "server offered %s authentication over a non-SSL connection",
+ mechanism_buf.data);
goto error;
}
}
+ else if (strcmp(mechanism_buf.data, SCRAM_SHA_512_NAME) == 0 &&
+ !selected_mechanism)
+ {
+ selected_mechanism = SCRAM_SHA_512_NAME;
+ conn->sasl = &pg_scram_mech;
+ }
else if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0 &&
!selected_mechanism)
{
@@ -517,7 +529,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)
}
if (conn->channel_binding[0] == 'r' && /* require */
- strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
+ strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0 &&
+ strcmp(selected_mechanism, SCRAM_SHA_512_PLUS_NAME) != 0)
{
libpq_append_conn_error(conn, "channel binding is required, but server did not offer an authentication method that supports channel binding");
goto error;
@@ -1249,11 +1262,14 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
/*
* Ok, now we know what algorithm to use
*/
- if (strcmp(algorithm, "scram-sha-256") == 0)
+ if (strcmp(algorithm, "scram-sha-256") == 0 ||
+ strcmp(algorithm, "scram-sha-512") == 0)
{
const char *errstr = NULL;
+ pg_cryptohash_type hash_type =
+ (strcmp(algorithm, "scram-sha-512") == 0) ? PG_SHA512 : PG_SHA256;
- crypt_pwd = pg_fe_scram_build_secret(passwd, &errstr);
+ crypt_pwd = pg_fe_scram_build_secret(passwd, hash_type, &errstr);
if (!crypt_pwd)
libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
}
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 049a8bb1a1..1f6dd52987 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -17,6 +17,7 @@
#include "libpq-fe.h"
#include "libpq-int.h"
+#include "common/cryptohash.h"
/* Prototypes for functions in fe-auth.c */
extern int pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn);
@@ -26,6 +27,7 @@ extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
/* Mechanisms in fe-auth-scram.c */
extern const pg_fe_sasl_mech pg_scram_mech;
extern char *pg_fe_scram_build_secret(const char *password,
+ pg_cryptohash_type hash_type,
const char **errstr);
#endif /* FE_AUTH_H */
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 42d3d4c79b..12026dd222 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -66,11 +66,14 @@ $node->init;
$node->append_conf('postgresql.conf', "log_connections = on\n");
$node->start;
-# Create 3 roles with different password methods for each one. The same
+# Create 4 roles with different password methods for each one. The same
# password is used for all of them.
$node->safe_psql('postgres',
"SET password_encryption='scram-sha-256'; CREATE ROLE scram_role LOGIN PASSWORD 'pass';"
);
+$node->safe_psql('postgres',
+ "SET password_encryption='scram-sha-512'; CREATE ROLE scram_role_512 LOGIN PASSWORD 'pass';"
+);
$node->safe_psql('postgres',
"SET password_encryption='md5'; CREATE ROLE md5_role LOGIN PASSWORD 'pass';"
);
@@ -134,6 +137,8 @@ test_conn(
log_like => [
qr/connection authenticated: identity="scram_role" method=scram-sha-256/
]);
+test_conn($node, 'user=scram_role_512', 'scram-sha-256', 2,
+ log_unlike => [qr/connection authenticated:/]);
test_conn($node, 'user=md5_role', 'scram-sha-256', 2,
log_unlike => [qr/connection authenticated:/]);
@@ -143,6 +148,20 @@ test_conn($node, 'user=scram_role', 'scram-sha-256', 2,
log_unlike => [qr/connection authenticated:/]);
$ENV{"PGPASSWORD"} = 'pass';
+# For "scram-sha-512" method, user "scram_role_512" should be able to connect.
+reset_pg_hba($node, 'all', 'all', 'scram-sha-512');
+test_conn(
+ $node,
+ 'user=scram_role_512',
+ 'scram-sha-512',
+ 0,
+ log_like => [
+ qr/connection authenticated: identity="scram_role_512" method=scram-sha-512/
+ ]);
+test_conn($node, 'user=scram_role_256', 'scram-sha-256', 2,
+ log_unlike => [qr/connection authenticated:/]);
+
+
# For "md5" method, all users should be able to connect (SCRAM
# authentication will be performed for the user with a SCRAM secret.)
reset_pg_hba($node, 'all', 'all', 'md5');
--
2.39.0