From 8e1a53287a75e0000a4823b6aac569f08dcf1599 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 15 Mar 2017 23:45:50 +0900 Subject: [PATCH 3/4] 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. --- src/backend/libpq/auth-scram.c | 57 ++---------------------------- src/backend/libpq/crypt.c | 7 +++- src/common/scram-common.c | 74 +++++++++++++++++++++++++++++++++++++++ src/include/common/scram-common.h | 20 +++++++++++ src/include/libpq/scram.h | 5 +-- 5 files changed, 103 insertions(+), 60 deletions(-) diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 29e2965883..4ed0e968d7 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -171,14 +171,14 @@ pg_be_scram_init(const char *username, const char *shadow_pass, bool doomed) } else if (password_type == PASSWORD_TYPE_PLAINTEXT) { - char *verifier; + char *verifier = palloc(SCRAM_VERIFIER_LEN + 1); /* * The password provided is in plain format, in which case a fresh * SCRAM verifier can be generated and used for the rest of the * processing. */ - verifier = scram_build_verifier(username, shadow_pass, 0); + (void) scram_build_verifier(username, shadow_pass, 0, verifier); (void) parse_scram_verifier(verifier, &state->salt, &state->iterations, state->StoredKey, state->ServerKey); @@ -316,59 +316,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); -} - /* * 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..9a448fad11 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -20,6 +20,7 @@ #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" @@ -168,7 +169,11 @@ encrypt_password(PasswordType target_type, const char *role, switch (guessed_type) { case PASSWORD_TYPE_PLAINTEXT: - return scram_build_verifier(role, password, 0); + encrypted_password = palloc(SCRAM_VERIFIER_LEN + 1); + if (!scram_build_verifier(role, password, 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..00e45a800a 100644 --- a/src/common/scram-common.c +++ b/src/common/scram-common.c @@ -24,6 +24,11 @@ #include #include "common/scram-common.h" +#ifndef FRONTEND +#include "utils/backend_random.h" +#else +#include "common/frontend_random.h" +#endif #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5C @@ -184,3 +189,72 @@ 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. + */ +bool +scram_build_verifier(const char *username, const char *password, + int iterations, char *verifier) +{ + uint8 keybuf[SCRAM_KEY_LEN + 1]; + char salt[SCRAM_SALT_LEN]; + char intbuf[SCRAM_ITERATION_LEN]; + char *p; + + if (iterations <= 0) + iterations = SCRAM_ITERATIONS_DEFAULT; + +#ifdef FRONTEND + if (!pg_frontend_random(salt, SCRAM_SALT_LEN)) + return false; +#else + if (!pg_backend_random(salt, SCRAM_SALT_LEN)) + { + ereport(LOG, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random salt"))); + return false; + } +#endif + + /* 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 7c98cc74d6..3fdfca44d6 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) */ @@ -41,6 +42,23 @@ #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 10 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 + \ + SCRAM_ITERATION_LEN + \ + pg_b64_enc_len(SCRAM_SALT_LEN) + \ + pg_b64_enc_len(SCRAM_KEY_LEN) * 2) + /* * Context data for HMAC used in SCRAM authentication. */ @@ -58,5 +76,7 @@ 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, + int iterations, char *verifier); #endif /* SCRAM_COMMON_H */ diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h index 78a52db684..3aaa9ef18a 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, boo 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); #endif /* PG_SCRAM_H */ -- 2.12.0