From 62235ef08fa8ce64f90e55e640df39c5b9d23f10 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Fri, 7 Apr 2017 10:10:15 +0900 Subject: [PATCH 2/5] Move routine to build SCRAM verifier into src/common/ This is aimed at being used by libpq to allow frontend-side creation of SCRAM verifiers. The result is not anymore allocated by the routine itself, the caller is responsible for that, similarly to md5. --- src/backend/libpq/auth-scram.c | 65 +++++++-------------------------------- src/backend/libpq/crypt.c | 21 +++++++++++-- src/common/scram-common.c | 56 +++++++++++++++++++++++++++++++++ src/include/common/scram-common.h | 20 ++++++++++++ src/include/libpq/scram.h | 5 +-- 5 files changed, 107 insertions(+), 60 deletions(-) diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 8fef0e995d..22f1e1a6c6 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -172,9 +172,18 @@ pg_be_scram_init(const char *username, const char *shadow_pass) * The stored password is in plain format. Generate a fresh SCRAM * verifier from it, and proceed with that. */ - char *verifier; + char *verifier = palloc(SCRAM_VERIFIER_LEN + 1); + char salt[SCRAM_SALT_LEN]; - verifier = scram_build_verifier(username, shadow_pass, 0); + if (!pg_backend_random(salt, SCRAM_SALT_LEN)) + { + ereport(LOG, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random salt"))); + return NULL; + } + + (void) scram_build_verifier(username, shadow_pass, salt, 0, verifier); (void) parse_scram_verifier(verifier, &state->salt, &state->iterations, state->StoredKey, state->ServerKey); @@ -328,58 +337,6 @@ pg_be_scram_exchange(void *opaq, char *input, int inputlen, return result; } -/* - * Construct a verifier string for SCRAM, stored in pg_authid.rolpassword. - * - * If iterations is 0, default number of iterations is used. The result is - * palloc'd, so caller is responsible for freeing it. - */ -char * -scram_build_verifier(const char *username, const char *password, - int iterations) -{ - uint8 keybuf[SCRAM_KEY_LEN + 1]; - char *encoded_storedkey; - char *encoded_serverkey; - char salt[SCRAM_SALT_LEN]; - char *encoded_salt; - int encoded_len; - - if (iterations <= 0) - iterations = SCRAM_ITERATIONS_DEFAULT; - - if (!pg_backend_random(salt, SCRAM_SALT_LEN)) - { - ereport(LOG, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("could not generate random salt"))); - return NULL; - } - - encoded_salt = palloc(pg_b64_enc_len(SCRAM_SALT_LEN) + 1); - encoded_len = pg_b64_encode(salt, SCRAM_SALT_LEN, encoded_salt); - encoded_salt[encoded_len] = '\0'; - - /* Calculate StoredKey, and encode it in base64 */ - encoded_storedkey = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1); - scram_ClientOrServerKey(password, salt, SCRAM_SALT_LEN, - iterations, SCRAM_CLIENT_KEY_NAME, keybuf); - scram_H(keybuf, SCRAM_KEY_LEN, keybuf); /* StoredKey */ - encoded_len = pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN, - encoded_storedkey); - encoded_storedkey[encoded_len] = '\0'; - - /* And same for ServerKey */ - encoded_serverkey = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1); - scram_ClientOrServerKey(password, salt, SCRAM_SALT_LEN, iterations, - SCRAM_SERVER_KEY_NAME, keybuf); - encoded_len = pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN, - encoded_serverkey); - encoded_serverkey[encoded_len] = '\0'; - - return psprintf("scram-sha-256:%s:%d:%s:%s", encoded_salt, iterations, - encoded_storedkey, encoded_serverkey); -} /* * Verify a plaintext password against a SCRAM verifier. This is used when diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 34beab5334..a0426f5450 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -20,9 +20,11 @@ #include "catalog/pg_authid.h" #include "common/md5.h" +#include "common/scram-common.h" #include "libpq/crypt.h" #include "libpq/scram.h" #include "miscadmin.h" +#include "utils/backend_random.h" #include "utils/builtins.h" #include "utils/syscache.h" #include "utils/timestamp.h" @@ -156,8 +158,23 @@ encrypt_password(PasswordType target_type, const char *role, switch (guessed_type) { case PASSWORD_TYPE_PLAINTEXT: - return scram_build_verifier(role, password, 0); - + { + char salt[SCRAM_SALT_LEN]; + + if (!pg_backend_random(salt, SCRAM_SALT_LEN)) + { + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random salt"))); + return NULL; + } + + encrypted_password = palloc(SCRAM_VERIFIER_LEN + 1); + if (!scram_build_verifier(role, password, salt, 0, + encrypted_password)) + elog(ERROR, "password encryption failed"); + return encrypted_password; + } case PASSWORD_TYPE_MD5: /* diff --git a/src/common/scram-common.c b/src/common/scram-common.c index e44f38f652..03bdc7b9d2 100644 --- a/src/common/scram-common.c +++ b/src/common/scram-common.c @@ -184,3 +184,59 @@ scram_ClientOrServerKey(const char *password, scram_HMAC_update(&ctx, keystr, strlen(keystr)); scram_HMAC_final(result, &ctx); } + +/* + * Construct a verifier string for SCRAM, stored in pg_authid.rolpassword. + * + * If iterations is 0, default number of iterations is used. The result is + * stored in "verifier" that caller is responsible to allocate a buffer of + * size SCRAM_VERIFIER_LEN. Returns true if the verifier has been generated, + * false otherwise. It is important for this routine to do no memory + * allocations. The salt defined by the caller needs to be a buffer pre-filled + * with random data of length SCRAM_SALT_LEN. + */ +bool +scram_build_verifier(const char *username, const char *password, + const char *salt, int iterations, char *verifier) +{ + uint8 keybuf[SCRAM_KEY_LEN + 1]; + char intbuf[12]; + char *p; + + if (iterations <= 0) + iterations = SCRAM_ITERATIONS_DEFAULT; + + /* Fill in the data of the verifier */ + p = verifier; + memcpy(p, SCRAM_VERIFIER_PREFIX, strlen(SCRAM_VERIFIER_PREFIX)); + p += strlen(SCRAM_VERIFIER_PREFIX); + *p++ = ':'; + + /* salt */ + (void) pg_b64_encode(salt, SCRAM_SALT_LEN, p); + p += pg_b64_enc_len(SCRAM_SALT_LEN); + *p++ = ':'; + + /* iterations */ + sprintf(intbuf, "%d", iterations); + memcpy(p, intbuf, strlen(intbuf)); + p += strlen(intbuf); + *p++ = ':'; + + /* Calculate StoredKey, and encode it in base64 */ + scram_ClientOrServerKey(password, salt, SCRAM_SALT_LEN, + iterations, SCRAM_CLIENT_KEY_NAME, keybuf); + scram_H(keybuf, SCRAM_KEY_LEN, keybuf); /* StoredKey */ + (void) pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN, p); + p += pg_b64_enc_len(SCRAM_KEY_LEN) - 1; + *p++ = ':'; + + /* And same for ServerKey */ + scram_ClientOrServerKey(password, salt, SCRAM_SALT_LEN, iterations, + SCRAM_SERVER_KEY_NAME, keybuf); + (void) pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN, p); + p += pg_b64_enc_len(SCRAM_KEY_LEN) - 1; + *p++ = '\0'; + + return true; +} diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h index 6740069eee..fcf3b98ba6 100644 --- a/src/include/common/scram-common.h +++ b/src/include/common/scram-common.h @@ -13,6 +13,7 @@ #ifndef SCRAM_COMMON_H #define SCRAM_COMMON_H +#include "common/base64.h" #include "common/sha2.h" /* Length of SCRAM keys (client and server) */ @@ -38,6 +39,22 @@ #define SCRAM_SERVER_KEY_NAME "Server Key" #define SCRAM_CLIENT_KEY_NAME "Client Key" +#define SCRAM_VERIFIER_PREFIX "scram-sha-256" + +/* + * Length of a SCRAM verifier, which is made of the following five fields + * separated by a colon: + * - prefix "scram-sha-256", made of 13 characters. + * - 4 colon separators. + * - 32-bit number of interations, up to 11 characters. + * - base64-encoded salt of length SCRAM_SALT_LEN + * - base64-encoded stored key of length SCRAM_KEY_LEN + * - base64-encoded server key of length SCRAM_KEY_LEN + */ +#define SCRAM_VERIFIER_LEN (strlen("scram-sha-256") + 4 + 11 + \ + pg_b64_enc_len(SCRAM_SALT_LEN) + \ + pg_b64_enc_len(SCRAM_KEY_LEN) * 2) + /* * Context data for HMAC used in SCRAM authentication. */ @@ -55,5 +72,8 @@ extern void scram_H(const uint8 *str, int len, uint8 *result); extern void scram_ClientOrServerKey(const char *password, const char *salt, int saltlen, int iterations, const char *keystr, uint8 *result); +extern bool scram_build_verifier(const char *username, const char *password, + const char *salt, int iterations, + char *verifier); #endif /* SCRAM_COMMON_H */ diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h index e373f0c07e..803fc4ef7a 100644 --- a/src/include/libpq/scram.h +++ b/src/include/libpq/scram.h @@ -26,10 +26,7 @@ extern void *pg_be_scram_init(const char *username, const char *shadow_pass); extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen, char **logdetail); -/* Routines to handle and check SCRAM-SHA-256 verifier */ -extern char *scram_build_verifier(const char *username, - const char *password, - int iterations); +/* Routines to check SCRAM-SHA-256 verifier */ extern bool is_scram_verifier(const char *verifier); extern bool scram_verify_plain_password(const char *username, const char *password, const char *verifier); -- 2.12.2