summaryrefslogtreecommitdiff
path: root/src/backend/libpq/auth-scram.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/auth-scram.c')
-rw-r--r--src/backend/libpq/auth-scram.c142
1 files changed, 90 insertions, 52 deletions
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 76c502d415..16bea446e3 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -5,6 +5,7 @@
*
* See the following RFCs for more details:
* - RFC 5802: https://tools.ietf.org/html/rfc5802
+ * - RFC 5803: https://tools.ietf.org/html/rfc5803
* - RFC 7677: https://tools.ietf.org/html/rfc7677
*
* Here are some differences:
@@ -19,7 +20,7 @@
* - Channel binding is not supported yet.
*
*
- * The password stored in pg_authid consists of the salt, iteration count,
+ * The password stored in pg_authid consists of the iteration count, salt,
* StoredKey and ServerKey.
*
* SASLprep usage
@@ -111,8 +112,8 @@ typedef struct
const char *username; /* username from startup packet */
- char *salt; /* base64-encoded */
int iterations;
+ char *salt; /* base64-encoded */
uint8 StoredKey[SCRAM_KEY_LEN];
uint8 ServerKey[SCRAM_KEY_LEN];
@@ -146,10 +147,10 @@ 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 bool parse_scram_verifier(const char *verifier, char **salt,
- int *iterations, uint8 *stored_key, uint8 *server_key);
-static void mock_scram_verifier(const char *username, char **salt, int *iterations,
- uint8 *stored_key, uint8 *server_key);
+static bool parse_scram_verifier(const char *verifier, int *iterations,
+ char **salt, uint8 *stored_key, uint8 *server_key);
+static void mock_scram_verifier(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);
static char *scram_MockSalt(const char *username);
@@ -185,7 +186,7 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
{
- if (parse_scram_verifier(shadow_pass, &state->salt, &state->iterations,
+ if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
state->StoredKey, state->ServerKey))
got_verifier = true;
else
@@ -208,7 +209,7 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
verifier = scram_build_verifier(username, shadow_pass, 0);
- (void) parse_scram_verifier(verifier, &state->salt, &state->iterations,
+ (void) parse_scram_verifier(verifier, &state->iterations, &state->salt,
state->StoredKey, state->ServerKey);
pfree(verifier);
@@ -243,7 +244,7 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
*/
if (!got_verifier)
{
- mock_scram_verifier(username, &state->salt, &state->iterations,
+ mock_scram_verifier(username, &state->iterations, &state->salt,
state->StoredKey, state->ServerKey);
state->doomed = true;
}
@@ -393,14 +394,15 @@ char *
scram_build_verifier(const char *username, const char *password,
int iterations)
{
- uint8 keybuf[SCRAM_KEY_LEN + 1];
- char storedkey_hex[SCRAM_KEY_LEN * 2 + 1];
- char serverkey_hex[SCRAM_KEY_LEN * 2 + 1];
- char salt[SCRAM_SALT_LEN];
- char *encoded_salt;
- int encoded_len;
char *prep_password = NULL;
pg_saslprep_rc rc;
+ char saltbuf[SCRAM_SALT_LEN];
+ uint8 keybuf[SCRAM_KEY_LEN];
+ char *encoded_salt;
+ char *encoded_storedkey;
+ char *encoded_serverkey;
+ int encoded_len;
+ char *result;
/*
* Normalize the password with SASLprep. If that doesn't work, because
@@ -414,7 +416,8 @@ scram_build_verifier(const char *username, const char *password,
if (iterations <= 0)
iterations = SCRAM_ITERATIONS_DEFAULT;
- if (!pg_backend_random(salt, SCRAM_SALT_LEN))
+ /* Generate salt, and encode it in base64 */
+ if (!pg_backend_random(saltbuf, SCRAM_SALT_LEN))
{
ereport(LOG,
(errcode(ERRCODE_INTERNAL_ERROR),
@@ -423,26 +426,38 @@ scram_build_verifier(const char *username, const char *password,
}
encoded_salt = palloc(pg_b64_enc_len(SCRAM_SALT_LEN) + 1);
- encoded_len = pg_b64_encode(salt, SCRAM_SALT_LEN, encoded_salt);
+ encoded_len = pg_b64_encode(saltbuf, SCRAM_SALT_LEN, encoded_salt);
encoded_salt[encoded_len] = '\0';
- /* Calculate StoredKey, and encode it in hex */
- scram_ClientOrServerKey(password, salt, SCRAM_SALT_LEN,
+ /* Calculate StoredKey, and encode it in base64 */
+ scram_ClientOrServerKey(password, saltbuf, SCRAM_SALT_LEN,
iterations, SCRAM_CLIENT_KEY_NAME, keybuf);
scram_H(keybuf, SCRAM_KEY_LEN, keybuf); /* StoredKey */
- (void) hex_encode((const char *) keybuf, SCRAM_KEY_LEN, storedkey_hex);
- storedkey_hex[SCRAM_KEY_LEN * 2] = '\0';
+
+ encoded_storedkey = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
+ encoded_len = pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN,
+ encoded_storedkey);
+ encoded_storedkey[encoded_len] = '\0';
/* And same for ServerKey */
- scram_ClientOrServerKey(password, salt, SCRAM_SALT_LEN, iterations,
+ scram_ClientOrServerKey(password, saltbuf, SCRAM_SALT_LEN, iterations,
SCRAM_SERVER_KEY_NAME, keybuf);
- (void) hex_encode((const char *) keybuf, SCRAM_KEY_LEN, serverkey_hex);
- serverkey_hex[SCRAM_KEY_LEN * 2] = '\0';
+
+ encoded_serverkey = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
+ encoded_len = pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN,
+ encoded_serverkey);
+ encoded_serverkey[encoded_len] = '\0';
+
+ result = psprintf("SCRAM-SHA-256$%d:%s$%s:%s", iterations, encoded_salt,
+ encoded_storedkey, encoded_serverkey);
if (prep_password)
pfree(prep_password);
+ pfree(encoded_salt);
+ pfree(encoded_storedkey);
+ pfree(encoded_serverkey);
- return psprintf("scram-sha-256:%s:%d:%s:%s", encoded_salt, iterations, storedkey_hex, serverkey_hex);
+ return result;
}
/*
@@ -464,7 +479,7 @@ scram_verify_plain_password(const char *username, const char *password,
char *prep_password = NULL;
pg_saslprep_rc rc;
- if (!parse_scram_verifier(verifier, &encoded_salt, &iterations,
+ if (!parse_scram_verifier(verifier, &iterations, &encoded_salt,
stored_key, server_key))
{
/*
@@ -509,13 +524,14 @@ scram_verify_plain_password(const char *username, const char *password,
bool
is_scram_verifier(const char *verifier)
{
- char *salt = NULL;
int iterations;
+ char *salt = NULL;
uint8 stored_key[SCRAM_KEY_LEN];
uint8 server_key[SCRAM_KEY_LEN];
bool result;
- result = parse_scram_verifier(verifier, &salt, &iterations, stored_key, server_key);
+ result = parse_scram_verifier(verifier, &iterations, &salt,
+ stored_key, server_key);
if (salt)
pfree(salt);
@@ -529,60 +545,82 @@ is_scram_verifier(const char *verifier)
* Returns true if the SCRAM verifier has been parsed, and false otherwise.
*/
static bool
-parse_scram_verifier(const char *verifier, char **salt, int *iterations,
+parse_scram_verifier(const char *verifier, int *iterations, char **salt,
uint8 *stored_key, uint8 *server_key)
{
char *v;
char *p;
+ char *scheme_str;
+ char *salt_str;
+ char *iterations_str;
+ char *storedkey_str;
+ char *serverkey_str;
+ int decoded_len;
+ char *decoded_salt_buf;
/*
* The verifier is of form:
*
- * scram-sha-256:<salt>:<iterations>:<storedkey>:<serverkey>
+ * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
*/
- if (strncmp(verifier, "scram-sha-256:", strlen("scram-sha-256:")) != 0)
- return false;
-
- v = pstrdup(verifier + strlen("scram-sha-256:"));
-
- /* salt */
- if ((p = strtok(v, ":")) == NULL)
+ v = pstrdup(verifier);
+ if ((scheme_str = strtok(v, "$")) == NULL)
+ goto invalid_verifier;
+ if ((iterations_str = strtok(NULL, ":")) == NULL)
+ goto invalid_verifier;
+ if ((salt_str = strtok(NULL, "$")) == NULL)
+ goto invalid_verifier;
+ if ((storedkey_str = strtok(NULL, ":")) == NULL)
+ goto invalid_verifier;
+ if ((serverkey_str = strtok(NULL, "")) == NULL)
goto invalid_verifier;
- *salt = pstrdup(p);
- /* iterations */
- if ((p = strtok(NULL, ":")) == NULL)
+ /* Parse the fields */
+ if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
goto invalid_verifier;
+
errno = 0;
- *iterations = strtol(p, &p, 10);
+ *iterations = strtol(iterations_str, &p, 10);
if (*p || errno != 0)
goto invalid_verifier;
- /* storedkey */
- if ((p = strtok(NULL, ":")) == NULL)
- goto invalid_verifier;
- if (strlen(p) != SCRAM_KEY_LEN * 2)
+ /*
+ * Verify that the salt is in Base64-encoded format, by decoding it,
+ * although we return the encoded version to the caller.
+ */
+ decoded_salt_buf = palloc(pg_b64_dec_len(strlen(salt_str)));
+ decoded_len = pg_b64_decode(salt_str, strlen(salt_str), decoded_salt_buf);
+ if (decoded_len < 0)
goto invalid_verifier;
+ *salt = pstrdup(salt_str);
- hex_decode(p, SCRAM_KEY_LEN * 2, (char *) stored_key);
+ /*
+ * Decode StoredKey and ServerKey.
+ */
+ if (pg_b64_dec_len(strlen(storedkey_str) != SCRAM_KEY_LEN))
+ goto invalid_verifier;
+ decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
+ (char *) stored_key);
+ if (decoded_len != SCRAM_KEY_LEN)
+ goto invalid_verifier;
- /* serverkey */
- if ((p = strtok(NULL, ":")) == NULL)
+ if (pg_b64_dec_len(strlen(serverkey_str) != SCRAM_KEY_LEN))
goto invalid_verifier;
- if (strlen(p) != SCRAM_KEY_LEN * 2)
+ decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
+ (char *) server_key);
+ if (decoded_len != SCRAM_KEY_LEN)
goto invalid_verifier;
- hex_decode(p, SCRAM_KEY_LEN * 2, (char *) server_key);
- pfree(v);
return true;
invalid_verifier:
pfree(v);
+ *salt = NULL;
return false;
}
static void
-mock_scram_verifier(const char *username, char **salt, int *iterations,
+mock_scram_verifier(const char *username, int *iterations, char **salt,
uint8 *stored_key, uint8 *server_key)
{
char *raw_salt;