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.
const char *client_pass,
char **logdetail)
{
- int retval;
char crypt_client_pass[MD5_PASSWD_LEN + 1];
/*
*/
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,
*/
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;
}