From c6305a9c572aaf3509ea90eec5127340dab89546 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Fri, 17 Mar 2017 11:33:27 +0200 Subject: [PATCH] Allow plaintext 'password' authentication when user has a SCRAM verifier. Oversight in the main SCRAM patch. --- src/backend/libpq/auth-scram.c | 46 +++++++++++++++++++++++++ src/backend/libpq/crypt.c | 61 ++++++++++++++++++++++------------ src/include/libpq/scram.h | 2 ++ 3 files changed, 87 insertions(+), 22 deletions(-) diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 9f78e57aae..db15a2fac6 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -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. diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 9f0ae15b00..ac10751ec2 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -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; } diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h index 78a52db684..fb21e056c8 100644 --- a/src/include/libpq/scram.h +++ b/src/include/libpq/scram.h @@ -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 */ -- 2.40.0