Allow plaintext 'password' authentication when user has a SCRAM verifier.

Oversight in the main SCRAM patch.
This commit is contained in:
Heikki Linnakangas 2017-03-17 11:33:27 +02:00
parent ff30aec759
commit c6305a9c57
3 changed files with 87 additions and 22 deletions

View File

@ -364,6 +364,52 @@ scram_build_verifier(const char *username, const char *password,
return psprintf("scram-sha-256:%s:%d:%s:%s", encoded_salt, iterations, storedkey_hex, serverkey_hex);
}
/*
* Verify a plaintext password against a SCRAM verifier. This is used when
* performing plaintext password authentication for a user that has a SCRAM
* verifier stored in pg_authid.
*/
bool
scram_verify_plain_password(const char *username, const char *password,
const char *verifier)
{
char *encoded_salt;
char *salt;
int saltlen;
int iterations;
uint8 stored_key[SCRAM_KEY_LEN];
uint8 server_key[SCRAM_KEY_LEN];
uint8 computed_key[SCRAM_KEY_LEN];
if (!parse_scram_verifier(verifier, &encoded_salt, &iterations,
stored_key, server_key))
{
/*
* The password looked like a SCRAM verifier, but could not be
* parsed.
*/
elog(LOG, "invalid SCRAM verifier for user \"%s\"", username);
return false;
}
salt = palloc(pg_b64_dec_len(strlen(encoded_salt)));
saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt);
if (saltlen == -1)
{
elog(LOG, "invalid SCRAM verifier for user \"%s\"", username);
return false;
}
/* Compute Server key based on the user-supplied plaintext password */
scram_ClientOrServerKey(password, salt, saltlen, iterations,
SCRAM_SERVER_KEY_NAME, computed_key);
/*
* Compare the verifier's Server Key with the one computed from the
* user-supplied password.
*/
return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
}
/*
* Check if given verifier can be used for SCRAM authentication.

View File

@ -283,7 +283,6 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
const char *client_pass,
char **logdetail)
{
int retval;
char crypt_client_pass[MD5_PASSWD_LEN + 1];
/*
@ -293,6 +292,21 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
*/
switch (get_password_type(shadow_pass))
{
case PASSWORD_TYPE_SCRAM:
if (scram_verify_plain_password(role,
client_pass,
shadow_pass))
{
return STATUS_OK;
}
else
{
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
role);
return STATUS_ERROR;
}
break;
case PASSWORD_TYPE_MD5:
if (!pg_md5_encrypt(client_pass,
role,
@ -307,30 +321,33 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
*/
return STATUS_ERROR;
}
client_pass = crypt_client_pass;
if (strcmp(crypt_client_pass, shadow_pass) == 0)
return STATUS_OK;
else
{
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
role);
return STATUS_ERROR;
}
break;
case PASSWORD_TYPE_PLAINTEXT:
if (strcmp(client_pass, shadow_pass) == 0)
return STATUS_OK;
else
{
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
role);
return STATUS_ERROR;
}
break;
default:
/*
* This shouldn't happen. Plain "password" authentication should
* be possible with any kind of stored password hash.
*/
*logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
role);
return STATUS_ERROR;
}
if (strcmp(client_pass, shadow_pass) == 0)
retval = STATUS_OK;
else
{
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
role);
retval = STATUS_ERROR;
}
return retval;
/*
* This shouldn't happen. Plain "password" authentication is possible
* with any kind of stored password hash.
*/
*logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
role);
return STATUS_ERROR;
}

View File

@ -31,5 +31,7 @@ extern char *scram_build_verifier(const char *username,
const char *password,
int iterations);
extern bool is_scram_verifier(const char *verifier);
extern bool scram_verify_plain_password(const char *username,
const char *password, const char *verifier);
#endif /* PG_SCRAM_H */