mirror of
https://github.com/postgres/postgres.git
synced 2025-08-19 23:22:23 +03:00
Fix use of term "verifier"
Within the context of SCRAM, "verifier" has a specific meaning in the protocol, per RFCs. The existing code used "verifier" differently, to mean whatever is or would be stored in pg_auth.rolpassword. Fix this by using the term "secret" for this, following RFC 5803. Reviewed-by: Michael Paquier <michael@paquier.xyz> Discussion: https://www.postgresql.org/message-id/flat/be397b06-6e4b-ba71-c7fb-54cae84a7e18%402ndquadrant.com
This commit is contained in:
@@ -64,10 +64,10 @@
|
||||
* Don't reveal user information to an unauthenticated client. We don't
|
||||
* want an attacker to be able to probe whether a particular username is
|
||||
* valid. In SCRAM, the server has to read the salt and iteration count
|
||||
* from the user's password verifier, and send it to the client. To avoid
|
||||
* from the user's stored secret, and send it to the client. To avoid
|
||||
* revealing whether a user exists, when the client tries to authenticate
|
||||
* with a username that doesn't exist, or doesn't have a valid SCRAM
|
||||
* verifier in pg_authid, we create a fake salt and iteration count
|
||||
* secret in pg_authid, we create a fake salt and iteration count
|
||||
* on-the-fly, and proceed with the authentication with that. In the end,
|
||||
* we'll reject the attempt, as if an incorrect password was given. When
|
||||
* we are performing a "mock" authentication, the 'doomed' flag in
|
||||
@@ -161,7 +161,7 @@ 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_verifier(const char *username, int *iterations,
|
||||
static void mock_scram_secret(const char *username, int *iterations,
|
||||
char **salt, uint8 *stored_key, uint8 *server_key);
|
||||
static bool is_scram_printable(char *p);
|
||||
static char *sanitize_char(char c);
|
||||
@@ -202,13 +202,13 @@ pg_be_scram_get_mechanisms(Port *port, StringInfo buf)
|
||||
*
|
||||
* Initialize a new SCRAM authentication exchange status tracker. This
|
||||
* needs to be called before doing any exchange. It will be filled later
|
||||
* after the beginning of the exchange with verifier data.
|
||||
* after the beginning of the exchange with authentication information.
|
||||
*
|
||||
* 'selected_mech' identifies the SASL mechanism that the client selected.
|
||||
* It should be one of the mechanisms that we support, as returned by
|
||||
* pg_be_scram_get_mechanisms().
|
||||
*
|
||||
* 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
|
||||
* 'shadow_pass' is the role's stored secret, from pg_authid.rolpassword.
|
||||
* The username was provided by the client in the startup message, and is
|
||||
* available in port->user_name. If 'shadow_pass' is NULL, we still perform
|
||||
* an authentication exchange, but it will fail, as if an incorrect password
|
||||
@@ -220,7 +220,7 @@ pg_be_scram_init(Port *port,
|
||||
const char *shadow_pass)
|
||||
{
|
||||
scram_state *state;
|
||||
bool got_verifier;
|
||||
bool got_secret;
|
||||
|
||||
state = (scram_state *) palloc0(sizeof(scram_state));
|
||||
state->port = port;
|
||||
@@ -248,7 +248,7 @@ pg_be_scram_init(Port *port,
|
||||
errmsg("client selected an invalid SASL authentication mechanism")));
|
||||
|
||||
/*
|
||||
* Parse the stored password verifier.
|
||||
* Parse the stored secret.
|
||||
*/
|
||||
if (shadow_pass)
|
||||
{
|
||||
@@ -256,30 +256,30 @@ pg_be_scram_init(Port *port,
|
||||
|
||||
if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
|
||||
{
|
||||
if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
|
||||
if (parse_scram_secret(shadow_pass, &state->iterations, &state->salt,
|
||||
state->StoredKey, state->ServerKey))
|
||||
got_verifier = true;
|
||||
got_secret = true;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The password looked like a SCRAM verifier, but could not be
|
||||
* The password looked like a SCRAM secret, but could not be
|
||||
* parsed.
|
||||
*/
|
||||
ereport(LOG,
|
||||
(errmsg("invalid SCRAM verifier for user \"%s\"",
|
||||
(errmsg("invalid SCRAM secret for user \"%s\"",
|
||||
state->port->user_name)));
|
||||
got_verifier = false;
|
||||
got_secret = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The user doesn't have SCRAM verifier. (You cannot do SCRAM
|
||||
* The user doesn't have SCRAM secret. (You cannot do SCRAM
|
||||
* authentication with an MD5 hash.)
|
||||
*/
|
||||
state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
|
||||
state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM secret."),
|
||||
state->port->user_name);
|
||||
got_verifier = false;
|
||||
got_secret = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -289,18 +289,18 @@ pg_be_scram_init(Port *port,
|
||||
* considered normal, since the caller requested it, so don't set log
|
||||
* detail.
|
||||
*/
|
||||
got_verifier = false;
|
||||
got_secret = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user did not have a valid SCRAM verifier, we still go through
|
||||
* If the user did not have a valid SCRAM secret, we still go through
|
||||
* the motions with a mock one, and fail as if the client supplied an
|
||||
* incorrect password. This is to avoid revealing information to an
|
||||
* attacker.
|
||||
*/
|
||||
if (!got_verifier)
|
||||
if (!got_secret)
|
||||
{
|
||||
mock_scram_verifier(state->port->user_name, &state->iterations,
|
||||
mock_scram_secret(state->port->user_name, &state->iterations,
|
||||
&state->salt, state->StoredKey, state->ServerKey);
|
||||
state->doomed = true;
|
||||
}
|
||||
@@ -443,12 +443,12 @@ pg_be_scram_exchange(void *opaq, const char *input, int inputlen,
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
|
||||
* Construct a SCRAM secret, for storing in pg_authid.rolpassword.
|
||||
*
|
||||
* The result is palloc'd, so caller is responsible for freeing it.
|
||||
*/
|
||||
char *
|
||||
pg_be_scram_build_verifier(const char *password)
|
||||
pg_be_scram_build_secret(const char *password)
|
||||
{
|
||||
char *prep_password;
|
||||
pg_saslprep_rc rc;
|
||||
@@ -470,7 +470,7 @@ pg_be_scram_build_verifier(const char *password)
|
||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("could not generate random salt")));
|
||||
|
||||
result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
|
||||
result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
|
||||
SCRAM_DEFAULT_ITERATIONS, password);
|
||||
|
||||
if (prep_password)
|
||||
@@ -480,13 +480,13 @@ pg_be_scram_build_verifier(const char *password)
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify a plaintext password against a SCRAM verifier. This is used when
|
||||
* Verify a plaintext password against a SCRAM secret. This is used when
|
||||
* performing plaintext password authentication for a user that has a SCRAM
|
||||
* verifier stored in pg_authid.
|
||||
* secret stored in pg_authid.
|
||||
*/
|
||||
bool
|
||||
scram_verify_plain_password(const char *username, const char *password,
|
||||
const char *verifier)
|
||||
const char *secret)
|
||||
{
|
||||
char *encoded_salt;
|
||||
char *salt;
|
||||
@@ -499,14 +499,14 @@ scram_verify_plain_password(const char *username, const char *password,
|
||||
char *prep_password;
|
||||
pg_saslprep_rc rc;
|
||||
|
||||
if (!parse_scram_verifier(verifier, &iterations, &encoded_salt,
|
||||
if (!parse_scram_secret(secret, &iterations, &encoded_salt,
|
||||
stored_key, server_key))
|
||||
{
|
||||
/*
|
||||
* The password looked like a SCRAM verifier, but could not be parsed.
|
||||
* The password looked like a SCRAM secret, but could not be parsed.
|
||||
*/
|
||||
ereport(LOG,
|
||||
(errmsg("invalid SCRAM verifier for user \"%s\"", username)));
|
||||
(errmsg("invalid SCRAM secret for user \"%s\"", username)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -517,7 +517,7 @@ scram_verify_plain_password(const char *username, const char *password,
|
||||
if (saltlen < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("invalid SCRAM verifier for user \"%s\"", username)));
|
||||
(errmsg("invalid SCRAM secret for user \"%s\"", username)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ scram_verify_plain_password(const char *username, const char *password,
|
||||
pfree(prep_password);
|
||||
|
||||
/*
|
||||
* Compare the verifier's Server Key with the one computed from the
|
||||
* Compare the secret's Server Key with the one computed from the
|
||||
* user-supplied password.
|
||||
*/
|
||||
return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
|
||||
@@ -542,18 +542,18 @@ scram_verify_plain_password(const char *username, const char *password,
|
||||
|
||||
|
||||
/*
|
||||
* Parse and validate format of given SCRAM verifier.
|
||||
* Parse and validate format of given SCRAM secret.
|
||||
*
|
||||
* On success, the iteration count, salt, stored key, and server key are
|
||||
* extracted from the verifier, and returned to the caller. For 'stored_key'
|
||||
* extracted from the secret, and returned to the caller. For 'stored_key'
|
||||
* and 'server_key', the caller must pass pre-allocated buffers of size
|
||||
* SCRAM_KEY_LEN. Salt is returned as a base64-encoded, null-terminated
|
||||
* string. The buffer for the salt is palloc'd by this function.
|
||||
*
|
||||
* Returns true if the SCRAM verifier has been parsed, and false otherwise.
|
||||
* Returns true if the SCRAM secret has been parsed, and false otherwise.
|
||||
*/
|
||||
bool
|
||||
parse_scram_verifier(const char *verifier, int *iterations, char **salt,
|
||||
parse_scram_secret(const char *secret, int *iterations, char **salt,
|
||||
uint8 *stored_key, uint8 *server_key)
|
||||
{
|
||||
char *v;
|
||||
@@ -569,30 +569,30 @@ parse_scram_verifier(const char *verifier, int *iterations, char **salt,
|
||||
char *decoded_server_buf;
|
||||
|
||||
/*
|
||||
* The verifier is of form:
|
||||
* The secret is of form:
|
||||
*
|
||||
* SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
|
||||
*/
|
||||
v = pstrdup(verifier);
|
||||
v = pstrdup(secret);
|
||||
if ((scheme_str = strtok(v, "$")) == NULL)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
if ((iterations_str = strtok(NULL, ":")) == NULL)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
if ((salt_str = strtok(NULL, "$")) == NULL)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
if ((storedkey_str = strtok(NULL, ":")) == NULL)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
if ((serverkey_str = strtok(NULL, "")) == NULL)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
|
||||
/* Parse the fields */
|
||||
if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
|
||||
errno = 0;
|
||||
*iterations = strtol(iterations_str, &p, 10);
|
||||
if (*p || errno != 0)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
|
||||
/*
|
||||
* Verify that the salt is in Base64-encoded format, by decoding it,
|
||||
@@ -603,7 +603,7 @@ parse_scram_verifier(const char *verifier, int *iterations, char **salt,
|
||||
decoded_len = pg_b64_decode(salt_str, strlen(salt_str),
|
||||
decoded_salt_buf, decoded_len);
|
||||
if (decoded_len < 0)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
*salt = pstrdup(salt_str);
|
||||
|
||||
/*
|
||||
@@ -614,7 +614,7 @@ parse_scram_verifier(const char *verifier, int *iterations, char **salt,
|
||||
decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
|
||||
decoded_stored_buf, decoded_len);
|
||||
if (decoded_len != SCRAM_KEY_LEN)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
memcpy(stored_key, decoded_stored_buf, SCRAM_KEY_LEN);
|
||||
|
||||
decoded_len = pg_b64_dec_len(strlen(serverkey_str));
|
||||
@@ -622,29 +622,29 @@ parse_scram_verifier(const char *verifier, int *iterations, char **salt,
|
||||
decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
|
||||
decoded_server_buf, decoded_len);
|
||||
if (decoded_len != SCRAM_KEY_LEN)
|
||||
goto invalid_verifier;
|
||||
goto invalid_secret;
|
||||
memcpy(server_key, decoded_server_buf, SCRAM_KEY_LEN);
|
||||
|
||||
return true;
|
||||
|
||||
invalid_verifier:
|
||||
invalid_secret:
|
||||
*salt = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate plausible SCRAM verifier parameters for mock authentication.
|
||||
* Generate plausible SCRAM secret parameters for mock authentication.
|
||||
*
|
||||
* In a normal authentication, these are extracted from the verifier
|
||||
* 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 verifier.
|
||||
* realistic, for when there is no stored secret.
|
||||
*
|
||||
* Like in parse_scram_verifier(), for 'stored_key' and 'server_key', the
|
||||
* Like in parse_scram_secret(), for 'stored_key' and 'server_key', the
|
||||
* caller must pass pre-allocated buffers of size SCRAM_KEY_LEN, and
|
||||
* the buffer for the salt is palloc'd by this function.
|
||||
*/
|
||||
static void
|
||||
mock_scram_verifier(const char *username, int *iterations, char **salt,
|
||||
mock_scram_secret(const char *username, int *iterations, char **salt,
|
||||
uint8 *stored_key, uint8 *server_key)
|
||||
{
|
||||
char *raw_salt;
|
||||
|
Reference in New Issue
Block a user