On Mon, Sep 28, 2020 at 12:55:06PM +0900, Michael Paquier wrote: > Thanks. I have done more tests with the range of OpenSSL versions we > support on HEAD, and applied this one. I have noticed that the > previous patch forgot two fail-and-abort code paths as of > EVP_DigestInit_ex() and EVP_DigestUpdate().
As this got reverted with fe0a1dc because of the lack of correct error reporting in libpq, I have restarted this work from scratch, and finished with the set of two patches attached. 0001 is a redesign of the APIs we use for the SHA2 implementations. The origin of the problem is that we cannot have a control of the memory context used by OpenSSL to allocate any of the EVP-related data, so we need to add some routines to be able to allocate and free the SHA2 contexts, basically. We have too many routines to do the work now, so I reduced the whole to 5, instead of 12 originally (this number would become 20 if we'd add the free/alloc routines for each SHA2 part), giving the following structure: /* Context Structures for SHA224/256/384/512 */ typedef enum { PG_SHA224 = 0, PG_SHA256, PG_SHA384, PG_SHA512 } pg_sha2_type; typedef struct pg_sha2_ctx { pg_sha2_type type; /* private area used by each SHA2 implementation */ void *data; } pg_sha2_ctx; extern pg_sha2_ctx *pg_sha2_create(pg_sha2_type type); extern int pg_sha2_init(pg_sha2_ctx *ctx); extern int pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len); extern int pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest); extern void pg_sha2_free(pg_sha2_ctx *ctx); A huge advantage of this approach is that the keep all the details of the SHA2 implementations within each file, so we have nothing related to OpenSSL in sha2.h, which is rather clean. All the internal structures part of the fallback implementations are also moved into their own file sha2.c. I have made the choice to limit the number of ifdef FRONTEND in the files of src/common/ for clarity, meaning that the callers of those routines can handle errors as they want, in the frontend and the backend. The areas making use of the SHA2 implementations are SCRAM (libpq and backend) and the checksum manifests, so this has required some rework of the existing interfaces to pass down errors correctly, but at the end the changes needed in libpq and pg_verifybackup are straight-forward. With 0001 in place, switching the SHA2 implementation of OpenSSL to use EVP is straight-forward, as the only thing that's actually needed here is to put in place a callback to clean up the EVP contexts allocated by OpenSSL. This is rather similar to what we do in pgcrypto in some ways, but that's actually simpler and I made things so as we only track down the EVP_MD_CTX members to free on abort. I'll add that to the next CF for review. -- Michael
From 44b3da02436b88a6e6cbb5671cafe2b0275c1eb5 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Wed, 14 Oct 2020 11:46:43 +0900 Subject: [PATCH 1/2] Rework SHA2 APIs This will make easier a switch to EVP for the OpenSSL SHA2 layer. (Note for self: this commit has been indented.) --- src/include/common/checksum_helper.h | 12 +- src/include/common/scram-common.h | 16 +- src/include/common/sha2.h | 60 ++---- src/include/replication/backup_manifest.h | 2 +- src/backend/libpq/auth-scram.c | 94 ++++++---- src/backend/replication/backup_manifest.c | 17 +- src/backend/replication/basebackup.c | 25 ++- src/backend/utils/adt/cryptohashes.c | 52 ++++-- src/common/checksum_helper.c | 79 ++++++-- src/common/scram-common.c | 166 ++++++++++++----- src/common/sha2.c | 204 +++++++++++++++++++-- src/common/sha2_openssl.c | 213 ++++++++++++++++------ src/bin/pg_verifybackup/parse_manifest.c | 14 +- src/bin/pg_verifybackup/pg_verifybackup.c | 24 ++- src/interfaces/libpq/fe-auth-scram.c | 114 +++++++----- contrib/pgcrypto/internal-sha2.c | 187 ++++--------------- 16 files changed, 818 insertions(+), 461 deletions(-) diff --git a/src/include/common/checksum_helper.h b/src/include/common/checksum_helper.h index 48b0745dad..4db7a1cce9 100644 --- a/src/include/common/checksum_helper.h +++ b/src/include/common/checksum_helper.h @@ -41,10 +41,10 @@ typedef enum pg_checksum_type typedef union pg_checksum_raw_context { pg_crc32c c_crc32c; - pg_sha224_ctx c_sha224; - pg_sha256_ctx c_sha256; - pg_sha384_ctx c_sha384; - pg_sha512_ctx c_sha512; + pg_sha2_ctx *c_sha224; + pg_sha2_ctx *c_sha256; + pg_sha2_ctx *c_sha384; + pg_sha2_ctx *c_sha512; } pg_checksum_raw_context; /* @@ -66,8 +66,8 @@ typedef struct pg_checksum_context extern bool pg_checksum_parse_type(char *name, pg_checksum_type *); extern char *pg_checksum_type_name(pg_checksum_type); -extern void pg_checksum_init(pg_checksum_context *, pg_checksum_type); -extern void pg_checksum_update(pg_checksum_context *, const uint8 *input, +extern int pg_checksum_init(pg_checksum_context *, pg_checksum_type); +extern int pg_checksum_update(pg_checksum_context *, const uint8 *input, size_t len); extern int pg_checksum_final(pg_checksum_context *, uint8 *output); diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h index 2edae2dd3c..53fc085a38 100644 --- a/src/include/common/scram-common.h +++ b/src/include/common/scram-common.h @@ -50,19 +50,19 @@ */ typedef struct { - pg_sha256_ctx sha256ctx; + pg_sha2_ctx *sha256ctx; uint8 k_opad[SHA256_HMAC_B]; } scram_HMAC_ctx; -extern void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen); -extern void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen); -extern void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx); +extern int scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen); +extern int scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen); +extern int scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx); -extern void scram_SaltedPassword(const char *password, const char *salt, +extern int scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result); -extern void scram_H(const uint8 *str, int len, uint8 *result); -extern void scram_ClientKey(const uint8 *salted_password, uint8 *result); -extern void scram_ServerKey(const uint8 *salted_password, uint8 *result); +extern int scram_H(const uint8 *str, int len, uint8 *result); +extern int scram_ClientKey(const uint8 *salted_password, uint8 *result); +extern int scram_ServerKey(const uint8 *salted_password, uint8 *result); extern char *scram_build_secret(const char *salt, int saltlen, int iterations, const char *password); diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h index 9c4abf777d..76940cba46 100644 --- a/src/include/common/sha2.h +++ b/src/include/common/sha2.h @@ -50,10 +50,6 @@ #ifndef _PG_SHA2_H_ #define _PG_SHA2_H_ -#ifdef USE_OPENSSL -#include <openssl/sha.h> -#endif - /*** SHA224/256/384/512 Various Length Definitions ***********************/ #define PG_SHA224_BLOCK_LENGTH 64 #define PG_SHA224_DIGEST_LENGTH 28 @@ -69,47 +65,25 @@ #define PG_SHA512_DIGEST_STRING_LENGTH (PG_SHA512_DIGEST_LENGTH * 2 + 1) /* Context Structures for SHA224/256/384/512 */ -#ifdef USE_OPENSSL -typedef SHA256_CTX pg_sha256_ctx; -typedef SHA512_CTX pg_sha512_ctx; -typedef SHA256_CTX pg_sha224_ctx; -typedef SHA512_CTX pg_sha384_ctx; -#else -typedef struct pg_sha256_ctx +typedef enum { - uint32 state[8]; - uint64 bitcount; - uint8 buffer[PG_SHA256_BLOCK_LENGTH]; -} pg_sha256_ctx; -typedef struct pg_sha512_ctx + PG_SHA224 = 0, + PG_SHA256, + PG_SHA384, + PG_SHA512 +} pg_sha2_type; + +typedef struct pg_sha2_ctx { - uint64 state[8]; - uint64 bitcount[2]; - uint8 buffer[PG_SHA512_BLOCK_LENGTH]; -} pg_sha512_ctx; -typedef struct pg_sha256_ctx pg_sha224_ctx; -typedef struct pg_sha512_ctx pg_sha384_ctx; -#endif /* USE_OPENSSL */ + pg_sha2_type type; + /* private area used by each SHA2 implementation */ + void *data; +} pg_sha2_ctx; -/* Interface routines for SHA224/256/384/512 */ -extern void pg_sha224_init(pg_sha224_ctx *ctx); -extern void pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *input0, - size_t len); -extern void pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest); - -extern void pg_sha256_init(pg_sha256_ctx *ctx); -extern void pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *input0, - size_t len); -extern void pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest); - -extern void pg_sha384_init(pg_sha384_ctx *ctx); -extern void pg_sha384_update(pg_sha384_ctx *ctx, - const uint8 *, size_t len); -extern void pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest); - -extern void pg_sha512_init(pg_sha512_ctx *ctx); -extern void pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *input0, - size_t len); -extern void pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest); +extern pg_sha2_ctx *pg_sha2_create(pg_sha2_type type); +extern int pg_sha2_init(pg_sha2_ctx *ctx); +extern int pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len); +extern int pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest); +extern void pg_sha2_free(pg_sha2_ctx *ctx); #endif /* _PG_SHA2_H_ */ diff --git a/src/include/replication/backup_manifest.h b/src/include/replication/backup_manifest.h index fb1291cbe4..eb4ef2ce76 100644 --- a/src/include/replication/backup_manifest.h +++ b/src/include/replication/backup_manifest.h @@ -28,7 +28,7 @@ typedef struct backup_manifest_info { BufFile *buffile; pg_checksum_type checksum_type; - pg_sha256_ctx manifest_ctx; + pg_sha2_ctx *manifest_ctx; uint64 manifest_size; bool force_encode; bool first_file; diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 0f79b28bb5..ed731b1674 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -527,8 +527,10 @@ scram_verify_plain_password(const char *username, const char *password, password = prep_password; /* Compute Server Key based on the user-supplied plaintext password */ - scram_SaltedPassword(password, salt, saltlen, iterations, salted_password); - scram_ServerKey(salted_password, computed_key); + if (scram_SaltedPassword(password, salt, saltlen, iterations, + salted_password) < 0 || + scram_ServerKey(salted_password, computed_key) < 0) + elog(ERROR, "could not compute server key"); if (prep_password) pfree(prep_password); @@ -653,6 +655,8 @@ mock_scram_secret(const char *username, int *iterations, char **salt, /* Generate deterministic salt */ raw_salt = scram_mock_salt(username); + if (raw_salt == NULL) + elog(ERROR, "could not encode salt"); /* same error as follows */ encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN); /* don't forget the zero-terminator */ @@ -1084,7 +1088,8 @@ verify_final_nonce(scram_state *state) /* * Verify the client proof contained in the last message received from - * client in an exchange. + * client in an exchange. Returns true if the verification is a success, + * or false for a failure. */ static bool verify_client_proof(scram_state *state) @@ -1095,27 +1100,33 @@ verify_client_proof(scram_state *state) scram_HMAC_ctx ctx; int i; - /* calculate ClientSignature */ - scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN); - scram_HMAC_update(&ctx, - state->client_first_message_bare, - strlen(state->client_first_message_bare)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->server_first_message, - strlen(state->server_first_message)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->client_final_message_without_proof, - strlen(state->client_final_message_without_proof)); - scram_HMAC_final(ClientSignature, &ctx); + /* + * Calculate ClientSignature. Note that we don't log directly a failure + * here even when processing the calculations as this could involve a mock + * authentication. + */ + if (scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN) < 0 || + scram_HMAC_update(&ctx, + state->client_first_message_bare, + strlen(state->client_first_message_bare)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->server_first_message, + strlen(state->server_first_message)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->client_final_message_without_proof, + strlen(state->client_final_message_without_proof)) < 0 || + scram_HMAC_final(ClientSignature, &ctx) < 0) + elog(ERROR, "could not calculate client signature"); /* Extract the ClientKey that the client calculated from the proof */ for (i = 0; i < SCRAM_KEY_LEN; i++) ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i]; /* Hash it one more time, and compare with StoredKey */ - scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey); + if (scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey) < 0) + elog(ERROR, "could not hash stored key"); if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0) return false; @@ -1346,19 +1357,22 @@ build_server_final_message(scram_state *state) scram_HMAC_ctx ctx; /* calculate ServerSignature */ - scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN); - scram_HMAC_update(&ctx, - state->client_first_message_bare, - strlen(state->client_first_message_bare)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->server_first_message, - strlen(state->server_first_message)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->client_final_message_without_proof, - strlen(state->client_final_message_without_proof)); - scram_HMAC_final(ServerSignature, &ctx); + if (scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN) < 0 || + scram_HMAC_update(&ctx, + state->client_first_message_bare, + strlen(state->client_first_message_bare)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->server_first_message, + strlen(state->server_first_message)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->client_final_message_without_proof, + strlen(state->client_final_message_without_proof)) < 0 || + scram_HMAC_final(ServerSignature, &ctx) < 0) + { + elog(ERROR, "could not calculate server signature"); + } siglen = pg_b64_enc_len(SCRAM_KEY_LEN); /* don't forget the zero-terminator */ @@ -1388,12 +1402,12 @@ build_server_final_message(scram_state *state) /* * Deterministically generate salt for mock authentication, using a SHA256 * hash based on the username and a cluster-level secret key. Returns a - * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN. + * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN, or NULL. */ static char * scram_mock_salt(const char *username) { - pg_sha256_ctx ctx; + pg_sha2_ctx *ctx; static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH]; char *mock_auth_nonce = GetMockAuthenticationNonce(); @@ -1406,10 +1420,16 @@ scram_mock_salt(const char *username) StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN, "salt length greater than SHA256 digest length"); - pg_sha256_init(&ctx); - pg_sha256_update(&ctx, (uint8 *) username, strlen(username)); - pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN); - pg_sha256_final(&ctx, sha_digest); + ctx = pg_sha2_create(PG_SHA256); + if (pg_sha2_init(ctx) < 0 || + pg_sha2_update(ctx, (uint8 *) username, strlen(username)) < 0 || + pg_sha2_update(ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN) < 0 || + pg_sha2_final(ctx, sha_digest) < 0) + { + pg_sha2_free(ctx); + return NULL; + } + pg_sha2_free(ctx); return (char *) sha_digest; } diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c index a43c793e28..2704a0db25 100644 --- a/src/backend/replication/backup_manifest.c +++ b/src/backend/replication/backup_manifest.c @@ -62,7 +62,9 @@ InitializeBackupManifest(backup_manifest_info *manifest, else manifest->buffile = BufFileCreateTemp(false); manifest->checksum_type = manifest_checksum_type; - pg_sha256_init(&manifest->manifest_ctx); + manifest->manifest_ctx = pg_sha2_create(PG_SHA256); + if (pg_sha2_init(manifest->manifest_ctx) < 0) + elog(ERROR, "failed to initialize checksum of backup manifest"); manifest->manifest_size = UINT64CONST(0); manifest->force_encode = (want_manifest == MANIFEST_OPTION_FORCE_ENCODE); manifest->first_file = true; @@ -161,6 +163,9 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid, int checksumlen; checksumlen = pg_checksum_final(checksum_ctx, checksumbuf); + if (checksumlen < 0) + elog(ERROR, "could not finalize checksum of file \"%s\"", + pathname); appendStringInfo(&buf, ", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"", @@ -305,7 +310,8 @@ SendBackupManifest(backup_manifest_info *manifest) * twice. */ manifest->still_checksumming = false; - pg_sha256_final(&manifest->manifest_ctx, checksumbuf); + if (pg_sha2_final(manifest->manifest_ctx, checksumbuf) < 0) + elog(ERROR, "failed to finalize checksum of backup manifest"); AppendStringToManifest(manifest, "\"Manifest-Checksum\": \""); hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf); checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH - 1] = '\0'; @@ -355,6 +361,8 @@ SendBackupManifest(backup_manifest_info *manifest) pq_putemptymessage('c'); /* Release resources */ + pg_sha2_free(manifest->manifest_ctx); + manifest->manifest_ctx = NULL; BufFileClose(manifest->buffile); } @@ -368,7 +376,10 @@ AppendStringToManifest(backup_manifest_info *manifest, char *s) Assert(manifest != NULL); if (manifest->still_checksumming) - pg_sha256_update(&manifest->manifest_ctx, (uint8 *) s, len); + { + if (pg_sha2_update(manifest->manifest_ctx, (uint8 *) s, len) < 0) + elog(ERROR, "failed to update checksum of backup manifest"); + } BufFileWrite(manifest->buffile, s, len); manifest->manifest_size += len; } diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index b89df01fa7..bbf912effb 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -414,7 +414,7 @@ perform_base_backup(basebackup_options *opt) if (ti->path == NULL) { struct stat statbuf; - bool sendtblspclinks = true; + bool sendtblspclinks = true; /* In the main tar, include the backup_label first... */ sendFileWithContent(BACKUP_LABEL_FILE, labelfile->data, @@ -1094,7 +1094,9 @@ sendFileWithContent(const char *filename, const char *content, len; pg_checksum_context checksum_ctx; - pg_checksum_init(&checksum_ctx, manifest->checksum_type); + if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0) + elog(ERROR, "could not initialize checksum of file \"%s\"", + filename); len = strlen(content); @@ -1130,7 +1132,10 @@ sendFileWithContent(const char *filename, const char *content, update_basebackup_progress(pad); } - pg_checksum_update(&checksum_ctx, (uint8 *) content, len); + if (pg_checksum_update(&checksum_ctx, (uint8 *) content, len) < 0) + elog(ERROR, "could not update checksum of file \"%s\"", + filename); + AddFileToBackupManifest(manifest, NULL, filename, len, (pg_time_t) statbuf.st_mtime, &checksum_ctx); } @@ -1584,7 +1589,9 @@ sendFile(const char *readfilename, const char *tarfilename, bool verify_checksum = false; pg_checksum_context checksum_ctx; - pg_checksum_init(&checksum_ctx, manifest->checksum_type); + if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0) + elog(ERROR, "could not initialize checksum of file \"%s\"", + readfilename); fd = OpenTransientFile(readfilename, O_RDONLY | PG_BINARY); if (fd < 0) @@ -1758,7 +1765,8 @@ sendFile(const char *readfilename, const char *tarfilename, update_basebackup_progress(cnt); /* Also feed it to the checksum machinery. */ - pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt); + if (pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt) < 0) + elog(ERROR, "could not update checksum of base backup"); len += cnt; throttle(cnt); @@ -1772,7 +1780,8 @@ sendFile(const char *readfilename, const char *tarfilename, { cnt = Min(sizeof(buf), statbuf->st_size - len); pq_putmessage('d', buf, cnt); - pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt); + if (pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt) < 0) + elog(ERROR, "could not update checksum of base backup"); update_basebackup_progress(cnt); len += cnt; throttle(cnt); @@ -1780,8 +1789,8 @@ sendFile(const char *readfilename, const char *tarfilename, } /* - * Pad to a block boundary, per tar format requirements. (This small - * piece of data is probably not worth throttling, and is not checksummed + * Pad to a block boundary, per tar format requirements. (This small piece + * of data is probably not worth throttling, and is not checksummed * because it's not actually part of the file.) */ pad = tarPaddingBytesRequired(len); diff --git a/src/backend/utils/adt/cryptohashes.c b/src/backend/utils/adt/cryptohashes.c index e897660927..16c61b3333 100644 --- a/src/backend/utils/adt/cryptohashes.c +++ b/src/backend/utils/adt/cryptohashes.c @@ -78,16 +78,21 @@ sha224_bytea(PG_FUNCTION_ARGS) bytea *in = PG_GETARG_BYTEA_PP(0); const uint8 *data; size_t len; - pg_sha224_ctx ctx; + pg_sha2_ctx *ctx; unsigned char buf[PG_SHA224_DIGEST_LENGTH]; bytea *result; len = VARSIZE_ANY_EXHDR(in); data = (unsigned char *) VARDATA_ANY(in); - pg_sha224_init(&ctx); - pg_sha224_update(&ctx, data, len); - pg_sha224_final(&ctx, buf); + ctx = pg_sha2_create(PG_SHA224); + if (pg_sha2_init(ctx) < 0) + elog(ERROR, "could not initialize %s context", "SHA224"); + if (pg_sha2_update(ctx, data, len) < 0) + elog(ERROR, "could not update %s context", "SHA224"); + if (pg_sha2_final(ctx, buf) < 0) + elog(ERROR, "could not finalize %s context", "SHA224"); + pg_sha2_free(ctx); result = palloc(sizeof(buf) + VARHDRSZ); SET_VARSIZE(result, sizeof(buf) + VARHDRSZ); @@ -102,16 +107,21 @@ sha256_bytea(PG_FUNCTION_ARGS) bytea *in = PG_GETARG_BYTEA_PP(0); const uint8 *data; size_t len; - pg_sha256_ctx ctx; + pg_sha2_ctx *ctx; unsigned char buf[PG_SHA256_DIGEST_LENGTH]; bytea *result; len = VARSIZE_ANY_EXHDR(in); data = (unsigned char *) VARDATA_ANY(in); - pg_sha256_init(&ctx); - pg_sha256_update(&ctx, data, len); - pg_sha256_final(&ctx, buf); + ctx = pg_sha2_create(PG_SHA256); + if (pg_sha2_init(ctx) < 0) + elog(ERROR, "could not initialize %s context", "SHA256"); + if (pg_sha2_update(ctx, data, len) < 0) + elog(ERROR, "could not update %s context", "SHA256"); + if (pg_sha2_final(ctx, buf) < 0) + elog(ERROR, "could not finalize %s context", "SHA256"); + pg_sha2_free(ctx); result = palloc(sizeof(buf) + VARHDRSZ); SET_VARSIZE(result, sizeof(buf) + VARHDRSZ); @@ -126,16 +136,21 @@ sha384_bytea(PG_FUNCTION_ARGS) bytea *in = PG_GETARG_BYTEA_PP(0); const uint8 *data; size_t len; - pg_sha384_ctx ctx; + pg_sha2_ctx *ctx; unsigned char buf[PG_SHA384_DIGEST_LENGTH]; bytea *result; len = VARSIZE_ANY_EXHDR(in); data = (unsigned char *) VARDATA_ANY(in); - pg_sha384_init(&ctx); - pg_sha384_update(&ctx, data, len); - pg_sha384_final(&ctx, buf); + ctx = pg_sha2_create(PG_SHA384); + if (pg_sha2_init(ctx) < 0) + elog(ERROR, "could not initialize %s context", "SHA384"); + if (pg_sha2_update(ctx, data, len) < 0) + elog(ERROR, "could not update %s context", "SHA384"); + if (pg_sha2_final(ctx, buf) < 0) + elog(ERROR, "could not finalize %s context", "SHA384"); + pg_sha2_free(ctx); result = palloc(sizeof(buf) + VARHDRSZ); SET_VARSIZE(result, sizeof(buf) + VARHDRSZ); @@ -150,16 +165,21 @@ sha512_bytea(PG_FUNCTION_ARGS) bytea *in = PG_GETARG_BYTEA_PP(0); const uint8 *data; size_t len; - pg_sha512_ctx ctx; + pg_sha2_ctx *ctx; unsigned char buf[PG_SHA512_DIGEST_LENGTH]; bytea *result; len = VARSIZE_ANY_EXHDR(in); data = (unsigned char *) VARDATA_ANY(in); - pg_sha512_init(&ctx); - pg_sha512_update(&ctx, data, len); - pg_sha512_final(&ctx, buf); + ctx = pg_sha2_create(PG_SHA512); + if (pg_sha2_init(ctx) < 0) + elog(ERROR, "could not initialize %s context", "SHA512"); + if (pg_sha2_update(ctx, data, len) < 0) + elog(ERROR, "could not update %s context", "SHA512"); + if (pg_sha2_final(ctx, buf) < 0) + elog(ERROR, "could not finalize %s context", "SHA512"); + pg_sha2_free(ctx); result = palloc(sizeof(buf) + VARHDRSZ); SET_VARSIZE(result, sizeof(buf) + VARHDRSZ); diff --git a/src/common/checksum_helper.c b/src/common/checksum_helper.c index 79a9a7447b..db50065a8a 100644 --- a/src/common/checksum_helper.c +++ b/src/common/checksum_helper.c @@ -77,8 +77,9 @@ pg_checksum_type_name(pg_checksum_type type) /* * Initialize a checksum context for checksums of the given type. + * Returns 0 for a success, -1 for a failure. */ -void +int pg_checksum_init(pg_checksum_context *context, pg_checksum_type type) { context->type = type; @@ -92,24 +93,55 @@ pg_checksum_init(pg_checksum_context *context, pg_checksum_type type) INIT_CRC32C(context->raw_context.c_crc32c); break; case CHECKSUM_TYPE_SHA224: - pg_sha224_init(&context->raw_context.c_sha224); + context->raw_context.c_sha224 = pg_sha2_create(PG_SHA224); + if (context->raw_context.c_sha224 == NULL) + return -1; + if (pg_sha2_init(context->raw_context.c_sha224) < 0) + { + pg_sha2_free(context->raw_context.c_sha224); + return -1; + } break; case CHECKSUM_TYPE_SHA256: - pg_sha256_init(&context->raw_context.c_sha256); + context->raw_context.c_sha256 = pg_sha2_create(PG_SHA256); + if (context->raw_context.c_sha256 == NULL) + return -1; + if (pg_sha2_init(context->raw_context.c_sha256) < 0) + { + pg_sha2_free(context->raw_context.c_sha256); + return -1; + } break; case CHECKSUM_TYPE_SHA384: - pg_sha384_init(&context->raw_context.c_sha384); + context->raw_context.c_sha384 = pg_sha2_create(PG_SHA384); + if (context->raw_context.c_sha384 == NULL) + return -1; + if (pg_sha2_init(context->raw_context.c_sha384) < 0) + { + pg_sha2_free(context->raw_context.c_sha384); + return -1; + } break; case CHECKSUM_TYPE_SHA512: - pg_sha512_init(&context->raw_context.c_sha512); + context->raw_context.c_sha512 = pg_sha2_create(PG_SHA512); + if (context->raw_context.c_sha512 == NULL) + return -1; + if (pg_sha2_init(context->raw_context.c_sha512) < 0) + { + pg_sha2_free(context->raw_context.c_sha512); + return -1; + } break; } + + return 0; } /* * Update a checksum context with new data. + * Returns 0 for a success, -1 for a failure. */ -void +int pg_checksum_update(pg_checksum_context *context, const uint8 *input, size_t len) { @@ -122,25 +154,32 @@ pg_checksum_update(pg_checksum_context *context, const uint8 *input, COMP_CRC32C(context->raw_context.c_crc32c, input, len); break; case CHECKSUM_TYPE_SHA224: - pg_sha224_update(&context->raw_context.c_sha224, input, len); + if (pg_sha2_update(context->raw_context.c_sha224, input, len) < 0) + return -1; break; case CHECKSUM_TYPE_SHA256: - pg_sha256_update(&context->raw_context.c_sha256, input, len); + if (pg_sha2_update(context->raw_context.c_sha256, input, len) < 0) + return -1; break; case CHECKSUM_TYPE_SHA384: - pg_sha384_update(&context->raw_context.c_sha384, input, len); + if (pg_sha2_update(context->raw_context.c_sha384, input, len) < 0) + return -1; break; case CHECKSUM_TYPE_SHA512: - pg_sha512_update(&context->raw_context.c_sha512, input, len); + if (pg_sha2_update(context->raw_context.c_sha512, input, len) < 0) + return -1; break; } + + return 0; } /* * Finalize a checksum computation and write the result to an output buffer. * * The caller must ensure that the buffer is at least PG_CHECKSUM_MAX_LENGTH - * bytes in length. The return value is the number of bytes actually written. + * bytes in length. The return value is the number of bytes actually written, + * or -1 for a failure. */ int pg_checksum_final(pg_checksum_context *context, uint8 *output) @@ -168,19 +207,27 @@ pg_checksum_final(pg_checksum_context *context, uint8 *output) memcpy(output, &context->raw_context.c_crc32c, retval); break; case CHECKSUM_TYPE_SHA224: - pg_sha224_final(&context->raw_context.c_sha224, output); + if (pg_sha2_final(context->raw_context.c_sha224, output) < 0) + return -1; + pg_sha2_free(context->raw_context.c_sha224); retval = PG_SHA224_DIGEST_LENGTH; break; case CHECKSUM_TYPE_SHA256: - pg_sha256_final(&context->raw_context.c_sha256, output); - retval = PG_SHA256_DIGEST_LENGTH; + if (pg_sha2_final(context->raw_context.c_sha256, output) < 0) + return -1; + pg_sha2_free(context->raw_context.c_sha256); + retval = PG_SHA224_DIGEST_LENGTH; break; case CHECKSUM_TYPE_SHA384: - pg_sha384_final(&context->raw_context.c_sha384, output); + if (pg_sha2_final(context->raw_context.c_sha384, output) < 0) + return -1; + pg_sha2_free(context->raw_context.c_sha384); retval = PG_SHA384_DIGEST_LENGTH; break; case CHECKSUM_TYPE_SHA512: - pg_sha512_final(&context->raw_context.c_sha512, output); + if (pg_sha2_final(context->raw_context.c_sha512, output) < 0) + return -1; + pg_sha2_free(context->raw_context.c_sha512); retval = PG_SHA512_DIGEST_LENGTH; break; } diff --git a/src/common/scram-common.c b/src/common/scram-common.c index 4971134b22..af2940eccf 100644 --- a/src/common/scram-common.c +++ b/src/common/scram-common.c @@ -13,6 +13,7 @@ * *------------------------------------------------------------------------- */ + #ifndef FRONTEND #include "postgres.h" #else @@ -29,9 +30,9 @@ /* * Calculate HMAC per RFC2104. * - * The hash function used is SHA-256. + * The hash function used is SHA-256. Returns 0 on success, -1 on failure. */ -void +int scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen) { uint8 k_ipad[SHA256_HMAC_B]; @@ -44,13 +45,21 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen) */ if (keylen > SHA256_HMAC_B) { - pg_sha256_ctx sha256_ctx; + pg_sha2_ctx *sha256_ctx; - pg_sha256_init(&sha256_ctx); - pg_sha256_update(&sha256_ctx, key, keylen); - pg_sha256_final(&sha256_ctx, keybuf); + sha256_ctx = pg_sha2_create(PG_SHA256); + if (sha256_ctx == NULL) + return -1; + if (pg_sha2_init(sha256_ctx) < 0 || + pg_sha2_update(sha256_ctx, key, keylen) < 0 || + pg_sha2_final(sha256_ctx, keybuf) < 0) + { + pg_sha2_free(sha256_ctx); + return -1; + } key = keybuf; keylen = SCRAM_KEY_LEN; + pg_sha2_free(sha256_ctx); } memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B); @@ -62,45 +71,75 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen) ctx->k_opad[i] ^= key[i]; } + ctx->sha256ctx = pg_sha2_create(PG_SHA256); + if (ctx->sha256ctx == NULL) + return -1; + /* tmp = H(K XOR ipad, text) */ - pg_sha256_init(&ctx->sha256ctx); - pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B); + if (pg_sha2_init(ctx->sha256ctx) < 0 || + pg_sha2_update(ctx->sha256ctx, k_ipad, SHA256_HMAC_B) < 0) + { + pg_sha2_free(ctx->sha256ctx); + return -1; + } + + return 0; } /* * Update HMAC calculation - * The hash function used is SHA-256. + * The hash function used is SHA-256. Returns 0 on success, -1 on failure. */ -void +int scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen) { - pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen); + Assert(ctx->sha256ctx != NULL); + if (pg_sha2_update(ctx->sha256ctx, (const uint8 *) str, slen) < 0) + { + pg_sha2_free(ctx->sha256ctx); + return -1; + } + return 0; } /* * Finalize HMAC calculation. - * The hash function used is SHA-256. + * The hash function used is SHA-256. Returns 0 on success, -1 on failure. */ -void +int scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx) { uint8 h[SCRAM_KEY_LEN]; - pg_sha256_final(&ctx->sha256ctx, h); + Assert(ctx->sha256ctx != NULL); + + if (pg_sha2_final(ctx->sha256ctx, h) < 0) + { + pg_sha2_free(ctx->sha256ctx); + return -1; + } /* H(K XOR opad, tmp) */ - pg_sha256_init(&ctx->sha256ctx); - pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B); - pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN); - pg_sha256_final(&ctx->sha256ctx, result); + if (pg_sha2_init(ctx->sha256ctx) < 0 || + pg_sha2_update(ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B) < 0 || + pg_sha2_update(ctx->sha256ctx, h, SCRAM_KEY_LEN) < 0 || + pg_sha2_final(ctx->sha256ctx, result) < 0) + { + pg_sha2_free(ctx->sha256ctx); + return -1; + } + + pg_sha2_free(ctx->sha256ctx); + return 0; } /* * Calculate SaltedPassword. * - * The password should already be normalized by SASLprep. + * The password should already be normalized by SASLprep. Returns 0 on + * success, -1 on failure. */ -void +int scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result) @@ -120,63 +159,86 @@ scram_SaltedPassword(const char *password, */ /* First iteration */ - scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); - scram_HMAC_update(&hmac_ctx, salt, saltlen); - scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)); - scram_HMAC_final(Ui_prev, &hmac_ctx); + if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 || + scram_HMAC_update(&hmac_ctx, salt, saltlen) < 0 || + scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)) < 0 || + scram_HMAC_final(Ui_prev, &hmac_ctx) < 0) + return -1; + memcpy(result, Ui_prev, SCRAM_KEY_LEN); /* Subsequent iterations */ for (i = 2; i <= iterations; i++) { - scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); - scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN); - scram_HMAC_final(Ui, &hmac_ctx); + if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 || + scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN) < 0 || + scram_HMAC_final(Ui, &hmac_ctx) < 0) + return -1; + for (j = 0; j < SCRAM_KEY_LEN; j++) result[j] ^= Ui[j]; memcpy(Ui_prev, Ui, SCRAM_KEY_LEN); } + + return 0; } /* * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is - * not included in the hash). + * not included in the hash). Returns 0 on success, -1 on failure. */ -void +int scram_H(const uint8 *input, int len, uint8 *result) { - pg_sha256_ctx ctx; + pg_sha2_ctx *ctx; - pg_sha256_init(&ctx); - pg_sha256_update(&ctx, input, len); - pg_sha256_final(&ctx, result); + ctx = pg_sha2_create(PG_SHA256); + if (ctx == NULL) + return -1; + + if (pg_sha2_init(ctx) < 0 || + pg_sha2_update(ctx, input, len) < 0 || + pg_sha2_final(ctx, result) < 0) + { + pg_sha2_free(ctx); + return -1; + } + + pg_sha2_free(ctx); + return 0; } /* - * Calculate ClientKey. + * Calculate ClientKey. Returns 0 on success, -1 on failure. */ -void +int scram_ClientKey(const uint8 *salted_password, uint8 *result) { scram_HMAC_ctx ctx; - scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); - scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")); - scram_HMAC_final(result, &ctx); + if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 || + scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")) < 0 || + scram_HMAC_final(result, &ctx) < 0) + return -1; + + return 0; } /* - * Calculate ServerKey. + * Calculate ServerKey. Returns 0 on success, -1 on failure. */ -void +int scram_ServerKey(const uint8 *salted_password, uint8 *result) { scram_HMAC_ctx ctx; - scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); - scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")); - scram_HMAC_final(result, &ctx); + if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 || + scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")) < 0 || + scram_HMAC_final(result, &ctx) < 0) + return -1; + + return 0; } @@ -207,12 +269,18 @@ scram_build_secret(const char *salt, int saltlen, int iterations, iterations = SCRAM_DEFAULT_ITERATIONS; /* Calculate StoredKey and ServerKey */ - scram_SaltedPassword(password, salt, saltlen, iterations, - salted_password); - scram_ClientKey(salted_password, stored_key); - scram_H(stored_key, SCRAM_KEY_LEN, stored_key); - - scram_ServerKey(salted_password, server_key); + if (scram_SaltedPassword(password, salt, saltlen, iterations, + salted_password) < 0 || + scram_ClientKey(salted_password, stored_key) < 0 || + scram_H(stored_key, SCRAM_KEY_LEN, stored_key) < 0 || + scram_ServerKey(salted_password, server_key) < 0) + { +#ifdef FRONTEND + return NULL; +#else + elog(ERROR, "could not calculate stored key and server key"); +#endif + } /*---------- * The format is: diff --git a/src/common/sha2.c b/src/common/sha2.c index 0d329bb238..5a0dfd7f8d 100644 --- a/src/common/sha2.c +++ b/src/common/sha2.c @@ -60,6 +60,34 @@ #include "common/sha2.h" +/* Internal structures used for the private area of pg_sha2_ctx->data */ +typedef struct pg_sha256_ctx +{ + uint32 state[8]; + uint64 bitcount; + uint8 buffer[PG_SHA256_BLOCK_LENGTH]; +} pg_sha256_ctx; +typedef struct pg_sha512_ctx +{ + uint64 state[8]; + uint64 bitcount[2]; + uint8 buffer[PG_SHA512_BLOCK_LENGTH]; +} pg_sha512_ctx; +typedef struct pg_sha256_ctx pg_sha224_ctx; +typedef struct pg_sha512_ctx pg_sha384_ctx; + +/* + * In backend, use palloc/pfree to ease the error handling. In frontend, + * use malloc to be able to return a failure status back to the caller. + */ +#ifndef FRONTEND +#define ALLOC(size) palloc(size) +#define FREE(ptr) pfree(ptr) +#else +#define ALLOC(size) malloc(size) +#define FREE(ptr) free(ptr) +#endif + /* * UNROLLED TRANSFORM LOOP NOTE: * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform @@ -264,7 +292,7 @@ static const uint64 sha512_initial_hash_value[8] = { /*** SHA-256: *********************************************************/ -void +static void pg_sha256_init(pg_sha256_ctx *context) { if (context == NULL) @@ -461,7 +489,7 @@ SHA256_Transform(pg_sha256_ctx *context, const uint8 *data) } #endif /* SHA2_UNROLL_TRANSFORM */ -void +static void pg_sha256_update(pg_sha256_ctx *context, const uint8 *data, size_t len) { size_t freespace, @@ -562,7 +590,7 @@ SHA256_Last(pg_sha256_ctx *context) SHA256_Transform(context, context->buffer); } -void +static void pg_sha256_final(pg_sha256_ctx *context, uint8 *digest) { /* If no digest buffer is passed, we don't bother doing this: */ @@ -590,7 +618,7 @@ pg_sha256_final(pg_sha256_ctx *context, uint8 *digest) /*** SHA-512: *********************************************************/ -void +static void pg_sha512_init(pg_sha512_ctx *context) { if (context == NULL) @@ -787,7 +815,7 @@ SHA512_Transform(pg_sha512_ctx *context, const uint8 *data) } #endif /* SHA2_UNROLL_TRANSFORM */ -void +static void pg_sha512_update(pg_sha512_ctx *context, const uint8 *data, size_t len) { size_t freespace, @@ -890,7 +918,7 @@ SHA512_Last(pg_sha512_ctx *context) SHA512_Transform(context, context->buffer); } -void +static void pg_sha512_final(pg_sha512_ctx *context, uint8 *digest) { /* If no digest buffer is passed, we don't bother doing this: */ @@ -919,7 +947,7 @@ pg_sha512_final(pg_sha512_ctx *context, uint8 *digest) /*** SHA-384: *********************************************************/ -void +static void pg_sha384_init(pg_sha384_ctx *context) { if (context == NULL) @@ -929,13 +957,13 @@ pg_sha384_init(pg_sha384_ctx *context) context->bitcount[0] = context->bitcount[1] = 0; } -void +static void pg_sha384_update(pg_sha384_ctx *context, const uint8 *data, size_t len) { pg_sha512_update((pg_sha512_ctx *) context, data, len); } -void +static void pg_sha384_final(pg_sha384_ctx *context, uint8 *digest) { /* If no digest buffer is passed, we don't bother doing this: */ @@ -963,7 +991,7 @@ pg_sha384_final(pg_sha384_ctx *context, uint8 *digest) } /*** SHA-224: *********************************************************/ -void +static void pg_sha224_init(pg_sha224_ctx *context) { if (context == NULL) @@ -973,13 +1001,13 @@ pg_sha224_init(pg_sha224_ctx *context) context->bitcount = 0; } -void +static void pg_sha224_update(pg_sha224_ctx *context, const uint8 *data, size_t len) { pg_sha256_update((pg_sha256_ctx *) context, data, len); } -void +static void pg_sha224_final(pg_sha224_ctx *context, uint8 *digest) { /* If no digest buffer is passed, we don't bother doing this: */ @@ -1004,3 +1032,155 @@ pg_sha224_final(pg_sha224_ctx *context, uint8 *digest) /* Clean up state data: */ memset(context, 0, sizeof(pg_sha224_ctx)); } + +/* External routines for this set of SHA2 implementations */ + +/* + * pg_sha2_create + * + * Allocate a SHA2 context. Returns NULL on failure. + */ +pg_sha2_ctx * +pg_sha2_create(pg_sha2_type type) +{ + pg_sha2_ctx *ctx; + + ctx = ALLOC(sizeof(pg_sha2_ctx)); + if (ctx == NULL) + return NULL; + + ctx->type = type; + + switch (type) + { + case PG_SHA224: + ctx->data = ALLOC(sizeof(pg_sha224_ctx)); + break; + case PG_SHA256: + ctx->data = ALLOC(sizeof(pg_sha256_ctx)); + break; + case PG_SHA384: + ctx->data = ALLOC(sizeof(pg_sha384_ctx)); + break; + case PG_SHA512: + ctx->data = ALLOC(sizeof(pg_sha512_ctx)); + break; + } + + if (ctx->data == NULL) + { + explicit_bzero(ctx, sizeof(pg_sha2_ctx)); + FREE(ctx); + return NULL; + } + + return ctx; +} + +/* + * pg_sha2_init + * + * Initialize a SHA2 context. Note that this implementation is designed + * to never fail, so this always returns 0. + */ +int +pg_sha2_init(pg_sha2_ctx *ctx) +{ + if (ctx == NULL) + return 0; + + switch (ctx->type) + { + case PG_SHA224: + pg_sha224_init((pg_sha224_ctx *) ctx->data); + break; + case PG_SHA256: + pg_sha256_init((pg_sha256_ctx *) ctx->data); + break; + case PG_SHA384: + pg_sha384_init((pg_sha384_ctx *) ctx->data); + break; + case PG_SHA512: + pg_sha512_init((pg_sha512_ctx *) ctx->data); + break; + } + + return 0; +} + +/* + * pg_sha2_update + * + * Update a SHA2 context. Note that this implementation is designed + * to never fail, so this always returns 0. + */ +int +pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len) +{ + if (ctx == NULL) + return 0; + + switch (ctx->type) + { + case PG_SHA224: + pg_sha224_update((pg_sha224_ctx *) ctx->data, data, len); + break; + case PG_SHA256: + pg_sha256_update((pg_sha256_ctx *) ctx->data, data, len); + break; + case PG_SHA384: + pg_sha384_update((pg_sha384_ctx *) ctx->data, data, len); + break; + case PG_SHA512: + pg_sha512_update((pg_sha512_ctx *) ctx->data, data, len); + break; + } + + return 0; +} + +/* + * pg_sha2_final + * + * Finalize a SHA2 context. Note that this implementation is designed + * to never fail, so this always returns 0. + */ +int +pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest) +{ + if (ctx == NULL) + return 0; + + switch (ctx->type) + { + case PG_SHA224: + pg_sha224_final((pg_sha224_ctx *) ctx->data, dest); + break; + case PG_SHA256: + pg_sha256_final((pg_sha256_ctx *) ctx->data, dest); + break; + case PG_SHA384: + pg_sha384_final((pg_sha384_ctx *) ctx->data, dest); + break; + case PG_SHA512: + pg_sha512_final((pg_sha512_ctx *) ctx->data, dest); + break; + } + + return 0; +} + +/* + * pg_sha2_free + * + * Free a SHA2 context. + */ +void +pg_sha2_free(pg_sha2_ctx *ctx) +{ + if (ctx == NULL) + return; + FREE(ctx->data); + explicit_bzero(ctx, sizeof(pg_sha2_ctx)); + FREE(ctx); +} diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c index 41673b3a88..57de96df90 100644 --- a/src/common/sha2_openssl.c +++ b/src/common/sha2_openssl.c @@ -24,79 +24,172 @@ #include "common/sha2.h" +/* + * In backend, use palloc/pfree to ease the error handling. In frontend, + * use malloc to be able to return a failure status back to the caller. + */ +#ifndef FRONTEND +#define ALLOC(size) palloc(size) +#define FREE(ptr) pfree(ptr) +#else +#define ALLOC(size) malloc(size) +#define FREE(ptr) free(ptr) +#endif -/* Interface routines for SHA-256 */ -void -pg_sha256_init(pg_sha256_ctx *ctx) +/* + * pg_sha2_create + * + * Allocate a SHA2 context. Returns NULL on failure. + */ +pg_sha2_ctx * +pg_sha2_create(pg_sha2_type type) { - SHA256_Init((SHA256_CTX *) ctx); + pg_sha2_ctx *ctx; + + ctx = ALLOC(sizeof(pg_sha2_ctx)); + if (ctx == NULL) + return NULL; + + ctx->type = type; + + switch (type) + { + case PG_SHA224: + case PG_SHA256: + ctx->data = ALLOC(sizeof(SHA256_CTX)); + break; + case PG_SHA384: + case PG_SHA512: + ctx->data = ALLOC(sizeof(SHA512_CTX)); + break; + } + + if (ctx->data == NULL) + { + explicit_bzero(ctx, sizeof(pg_sha2_ctx)); + FREE(ctx); + return NULL; + } + + return ctx; } -void -pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len) +/* + * pg_sha2_init + * + * Initialize a SHA2 context. Returns 0 on success, and -1 on failure. + */ +int +pg_sha2_init(pg_sha2_ctx *ctx) { - SHA256_Update((SHA256_CTX *) ctx, data, len); + int status = 0; + + if (ctx == NULL) + return 0; + + switch (ctx->type) + { + case PG_SHA224: + status = SHA224_Init((SHA256_CTX *) ctx->data); + break; + case PG_SHA256: + status = SHA256_Init((SHA256_CTX *) ctx->data); + break; + case PG_SHA384: + status = SHA384_Init((SHA512_CTX *) ctx->data); + break; + case PG_SHA512: + status = SHA512_Init((SHA512_CTX *) ctx->data); + break; + } + + /* OpenSSL internals return 1 on success, 0 on failure */ + if (status <= 0) + return -1; + return 0; } -void -pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest) +/* + * pg_sha2_update + * + * Update a SHA2 context. Returns 0 on success, and -1 on failure. + */ +int +pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len) { - SHA256_Final(dest, (SHA256_CTX *) ctx); + int status; + + if (ctx == NULL) + return 0; + + switch (ctx->type) + { + case PG_SHA224: + status = SHA224_Update((SHA256_CTX *) ctx->data, data, len); + break; + case PG_SHA256: + status = SHA256_Update((SHA256_CTX *) ctx->data, data, len); + break; + case PG_SHA384: + status = SHA384_Update((SHA512_CTX *) ctx->data, data, len); + break; + case PG_SHA512: + status = SHA512_Update((SHA512_CTX *) ctx->data, data, len); + break; + } + + /* OpenSSL internals return 1 on success, 0 on failure */ + if (status <= 0) + return -1; + return 0; } -/* Interface routines for SHA-512 */ -void -pg_sha512_init(pg_sha512_ctx *ctx) +/* + * pg_sha2_final + * + * Finalize a SHA2 context. Returns 0 on success, and -1 on failure. + */ +int +pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest) { - SHA512_Init((SHA512_CTX *) ctx); + int status; + + if (ctx == NULL) + return 0; + + switch (ctx->type) + { + case PG_SHA224: + status = SHA224_Final(dest, (SHA256_CTX *) ctx->data); + break; + case PG_SHA256: + status = SHA256_Final(dest, (SHA256_CTX *) ctx->data); + break; + case PG_SHA384: + status = SHA384_Final(dest, (SHA512_CTX *) ctx->data); + break; + case PG_SHA512: + status = SHA512_Final(dest, (SHA512_CTX *) ctx->data); + break; + } + + /* OpenSSL internals return 1 on success, 0 on failure */ + if (status <= 0) + return -1; + return 0; } +/* + * pg_sha2_free + * + * Free a SHA2 context. + */ void -pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len) +pg_sha2_free(pg_sha2_ctx *ctx) { - SHA512_Update((SHA512_CTX *) ctx, data, len); -} - -void -pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest) -{ - SHA512_Final(dest, (SHA512_CTX *) ctx); -} - -/* Interface routines for SHA-384 */ -void -pg_sha384_init(pg_sha384_ctx *ctx) -{ - SHA384_Init((SHA512_CTX *) ctx); -} - -void -pg_sha384_update(pg_sha384_ctx *ctx, const uint8 *data, size_t len) -{ - SHA384_Update((SHA512_CTX *) ctx, data, len); -} - -void -pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest) -{ - SHA384_Final(dest, (SHA512_CTX *) ctx); -} - -/* Interface routines for SHA-224 */ -void -pg_sha224_init(pg_sha224_ctx *ctx) -{ - SHA224_Init((SHA256_CTX *) ctx); -} - -void -pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *data, size_t len) -{ - SHA224_Update((SHA256_CTX *) ctx, data, len); -} - -void -pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest) -{ - SHA224_Final(dest, (SHA256_CTX *) ctx); + if (ctx == NULL) + return; + FREE(ctx->data); + explicit_bzero(ctx, sizeof(pg_sha2_ctx)); + FREE(ctx); } diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c index 608e23538b..2b1bf36e2d 100644 --- a/src/bin/pg_verifybackup/parse_manifest.c +++ b/src/bin/pg_verifybackup/parse_manifest.c @@ -624,7 +624,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer, size_t number_of_newlines = 0; size_t ultimate_newline = 0; size_t penultimate_newline = 0; - pg_sha256_ctx manifest_ctx; + pg_sha2_ctx *manifest_ctx; uint8 manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH]; uint8 manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH]; @@ -652,9 +652,15 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer, "last line not newline-terminated"); /* Checksum the rest. */ - pg_sha256_init(&manifest_ctx); - pg_sha256_update(&manifest_ctx, (uint8 *) buffer, penultimate_newline + 1); - pg_sha256_final(&manifest_ctx, manifest_checksum_actual); + manifest_ctx = pg_sha2_create(PG_SHA256); + if (manifest_ctx == NULL) + context->error_cb(context, "out of memory"); + if (pg_sha2_init(manifest_ctx) < 0) + context->error_cb(context, "could not initialize checksum of manifest"); + if (pg_sha2_update(manifest_ctx, (uint8 *) buffer, penultimate_newline + 1) < 0) + context->error_cb(context, "could not update checksum of manifest"); + if (pg_sha2_final(manifest_ctx, manifest_checksum_actual) < 0) + context->error_cb(context, "could not finalize checksum of manifest"); /* Now verify it. */ if (parse->manifest_checksum == NULL) diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c index bb3733b57e..07320d3699 100644 --- a/src/bin/pg_verifybackup/pg_verifybackup.c +++ b/src/bin/pg_verifybackup/pg_verifybackup.c @@ -726,13 +726,26 @@ verify_file_checksum(verifier_context *context, manifest_file *m, } /* Initialize checksum context. */ - pg_checksum_init(&checksum_ctx, m->checksum_type); + if (pg_checksum_init(&checksum_ctx, m->checksum_type) < 0) + { + report_backup_error(context, "could not initialize checksum of file \"%s\"", + relpath); + return; + } /* Read the file chunk by chunk, updating the checksum as we go. */ while ((rc = read(fd, buffer, READ_CHUNK_SIZE)) > 0) { bytes_read += rc; - pg_checksum_update(&checksum_ctx, buffer, rc); + if (pg_checksum_update(&checksum_ctx, buffer, rc) < 0) + { + report_backup_error(context, "could not update checksum of file \"%s\"", + relpath); + close(fd); + return; + } + + } if (rc < 0) report_backup_error(context, "could not read file \"%s\": %m", @@ -767,6 +780,13 @@ verify_file_checksum(verifier_context *context, manifest_file *m, /* Get the final checksum. */ checksumlen = pg_checksum_final(&checksum_ctx, checksumbuf); + if (checksumlen < 0) + { + report_backup_error(context, + "could not finalize checksum of file \"%s\"", + relpath); + return; + } /* And check it against the manifest. */ if (checksumlen != m->checksum_length) diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index 6d266e9796..0a216cbe84 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -63,8 +63,8 @@ static bool read_server_first_message(fe_scram_state *state, char *input); static bool read_server_final_message(fe_scram_state *state, char *input); static char *build_client_first_message(fe_scram_state *state); static char *build_client_final_message(fe_scram_state *state); -static bool verify_server_signature(fe_scram_state *state); -static void calculate_client_proof(fe_scram_state *state, +static bool verify_server_signature(fe_scram_state *state, bool *match); +static bool calculate_client_proof(fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result); @@ -256,11 +256,15 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen, * Verify server signature, to make sure we're talking to the * genuine server. */ - if (verify_server_signature(state)) - *success = true; - else + if (!verify_server_signature(state, success)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not verify server signature\n")); + goto error; + } + + if (!*success) { - *success = false; printfPQExpBuffer(&conn->errorMessage, libpq_gettext("incorrect server signature\n")); } @@ -544,9 +548,15 @@ build_client_final_message(fe_scram_state *state) goto oom_error; /* Append proof to it, to form client-final-message. */ - calculate_client_proof(state, - state->client_final_message_without_proof, - client_proof); + if (!calculate_client_proof(state, + state->client_final_message_without_proof, + client_proof)) + { + termPQExpBuffer(&buf); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not calculate client proof\n")); + return NULL; + } appendPQExpBufferStr(&buf, ",p="); encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN); @@ -745,9 +755,9 @@ read_server_final_message(fe_scram_state *state, char *input) /* * Calculate the client proof, part of the final exchange message sent - * by the client. + * by the client. Returns true on success, false on failure. */ -static void +static bool calculate_client_proof(fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result) @@ -762,61 +772,67 @@ calculate_client_proof(fe_scram_state *state, * Calculate SaltedPassword, and store it in 'state' so that we can reuse * it later in verify_server_signature. */ - scram_SaltedPassword(state->password, state->salt, state->saltlen, - state->iterations, state->SaltedPassword); - - scram_ClientKey(state->SaltedPassword, ClientKey); - scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey); - - scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN); - scram_HMAC_update(&ctx, - state->client_first_message_bare, - strlen(state->client_first_message_bare)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->server_first_message, - strlen(state->server_first_message)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - client_final_message_without_proof, - strlen(client_final_message_without_proof)); - scram_HMAC_final(ClientSignature, &ctx); + if (scram_SaltedPassword(state->password, state->salt, state->saltlen, + state->iterations, state->SaltedPassword) < 0 || + scram_ClientKey(state->SaltedPassword, ClientKey) < 0 || + scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey) < 0 || + scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN) < 0 || + scram_HMAC_update(&ctx, + state->client_first_message_bare, + strlen(state->client_first_message_bare)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->server_first_message, + strlen(state->server_first_message)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + client_final_message_without_proof, + strlen(client_final_message_without_proof)) < 0 || + scram_HMAC_final(ClientSignature, &ctx) < 0) + return false; for (i = 0; i < SCRAM_KEY_LEN; i++) result[i] = ClientKey[i] ^ ClientSignature[i]; + + return true; } /* * Validate the server signature, received as part of the final exchange - * message received from the server. + * message received from the server. *match tracks if the server signature + * matched or not. Returns true if the server signature got verified, and + * false for a processing error. */ static bool -verify_server_signature(fe_scram_state *state) +verify_server_signature(fe_scram_state *state, bool *match) { uint8 expected_ServerSignature[SCRAM_KEY_LEN]; uint8 ServerKey[SCRAM_KEY_LEN]; scram_HMAC_ctx ctx; - scram_ServerKey(state->SaltedPassword, ServerKey); - + if (scram_ServerKey(state->SaltedPassword, ServerKey) < 0 || /* calculate ServerSignature */ - scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN); - scram_HMAC_update(&ctx, - state->client_first_message_bare, - strlen(state->client_first_message_bare)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->server_first_message, - strlen(state->server_first_message)); - scram_HMAC_update(&ctx, ",", 1); - scram_HMAC_update(&ctx, - state->client_final_message_without_proof, - strlen(state->client_final_message_without_proof)); - scram_HMAC_final(expected_ServerSignature, &ctx); - - if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0) + scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN) < 0 || + scram_HMAC_update(&ctx, + state->client_first_message_bare, + strlen(state->client_first_message_bare)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->server_first_message, + strlen(state->server_first_message)) < 0 || + scram_HMAC_update(&ctx, ",", 1) < 0 || + scram_HMAC_update(&ctx, + state->client_final_message_without_proof, + strlen(state->client_final_message_without_proof)) < 0 || + scram_HMAC_final(expected_ServerSignature, &ctx) < 0) return false; + /* signature processed, so now check after it */ + if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0) + *match = false; + else + *match = true; + return true; } diff --git a/contrib/pgcrypto/internal-sha2.c b/contrib/pgcrypto/internal-sha2.c index 9fa940b5bb..9037dfa4a6 100644 --- a/contrib/pgcrypto/internal-sha2.c +++ b/contrib/pgcrypto/internal-sha2.c @@ -42,7 +42,6 @@ void init_sha384(PX_MD *h); void init_sha512(PX_MD *h); /* SHA224 */ - static unsigned int_sha224_len(PX_MD *h) { @@ -55,42 +54,7 @@ int_sha224_block_len(PX_MD *h) return PG_SHA224_BLOCK_LENGTH; } -static void -int_sha224_update(PX_MD *h, const uint8 *data, unsigned dlen) -{ - pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - - pg_sha224_update(ctx, data, dlen); -} - -static void -int_sha224_reset(PX_MD *h) -{ - pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - - pg_sha224_init(ctx); -} - -static void -int_sha224_finish(PX_MD *h, uint8 *dst) -{ - pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - - pg_sha224_final(ctx, dst); -} - -static void -int_sha224_free(PX_MD *h) -{ - pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - - px_memset(ctx, 0, sizeof(*ctx)); - pfree(ctx); - pfree(h); -} - /* SHA256 */ - static unsigned int_sha256_len(PX_MD *h) { @@ -103,42 +67,7 @@ int_sha256_block_len(PX_MD *h) return PG_SHA256_BLOCK_LENGTH; } -static void -int_sha256_update(PX_MD *h, const uint8 *data, unsigned dlen) -{ - pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - - pg_sha256_update(ctx, data, dlen); -} - -static void -int_sha256_reset(PX_MD *h) -{ - pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - - pg_sha256_init(ctx); -} - -static void -int_sha256_finish(PX_MD *h, uint8 *dst) -{ - pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - - pg_sha256_final(ctx, dst); -} - -static void -int_sha256_free(PX_MD *h) -{ - pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - - px_memset(ctx, 0, sizeof(*ctx)); - pfree(ctx); - pfree(h); -} - /* SHA384 */ - static unsigned int_sha384_len(PX_MD *h) { @@ -151,42 +80,7 @@ int_sha384_block_len(PX_MD *h) return PG_SHA384_BLOCK_LENGTH; } -static void -int_sha384_update(PX_MD *h, const uint8 *data, unsigned dlen) -{ - pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - - pg_sha384_update(ctx, data, dlen); -} - -static void -int_sha384_reset(PX_MD *h) -{ - pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - - pg_sha384_init(ctx); -} - -static void -int_sha384_finish(PX_MD *h, uint8 *dst) -{ - pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - - pg_sha384_final(ctx, dst); -} - -static void -int_sha384_free(PX_MD *h) -{ - pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - - px_memset(ctx, 0, sizeof(*ctx)); - pfree(ctx); - pfree(h); -} - /* SHA512 */ - static unsigned int_sha512_len(PX_MD *h) { @@ -199,37 +93,40 @@ int_sha512_block_len(PX_MD *h) return PG_SHA512_BLOCK_LENGTH; } +/* Generic interface for all SHA2 methods */ static void -int_sha512_update(PX_MD *h, const uint8 *data, unsigned dlen) +int_sha2_update(PX_MD *h, const uint8 *data, unsigned dlen) { - pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; + pg_sha2_ctx *ctx = (pg_sha2_ctx *) h->p.ptr; - pg_sha512_update(ctx, data, dlen); + if (pg_sha2_update(ctx, data, dlen) < 0) + elog(ERROR, "could not update %s context", "SHA2"); } static void -int_sha512_reset(PX_MD *h) +int_sha2_reset(PX_MD *h) { - pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; + pg_sha2_ctx *ctx = (pg_sha2_ctx *) h->p.ptr; - pg_sha512_init(ctx); + if (pg_sha2_init(ctx) < 0) + elog(ERROR, "could not initialize %s context", "SHA2"); } static void -int_sha512_finish(PX_MD *h, uint8 *dst) +int_sha2_finish(PX_MD *h, uint8 *dst) { - pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; + pg_sha2_ctx *ctx = (pg_sha2_ctx *) h->p.ptr; - pg_sha512_final(ctx, dst); + if (pg_sha2_final(ctx, dst) < 0) + elog(ERROR, "could not finalize %s context", "SHA2"); } static void -int_sha512_free(PX_MD *h) +int_sha2_free(PX_MD *h) { - pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; + pg_sha2_ctx *ctx = (pg_sha2_ctx *) h->p.ptr; - px_memset(ctx, 0, sizeof(*ctx)); - pfree(ctx); + pg_sha2_free(ctx); pfree(h); } @@ -238,18 +135,17 @@ int_sha512_free(PX_MD *h) void init_sha224(PX_MD *md) { - pg_sha224_ctx *ctx; - - ctx = palloc0(sizeof(*ctx)); + pg_sha2_ctx *ctx; + ctx = pg_sha2_create(PG_SHA224); md->p.ptr = ctx; md->result_size = int_sha224_len; md->block_size = int_sha224_block_len; - md->reset = int_sha224_reset; - md->update = int_sha224_update; - md->finish = int_sha224_finish; - md->free = int_sha224_free; + md->reset = int_sha2_reset; + md->update = int_sha2_update; + md->finish = int_sha2_finish; + md->free = int_sha2_free; md->reset(md); } @@ -257,18 +153,17 @@ init_sha224(PX_MD *md) void init_sha256(PX_MD *md) { - pg_sha256_ctx *ctx; - - ctx = palloc0(sizeof(*ctx)); + pg_sha2_ctx *ctx; + ctx = pg_sha2_create(PG_SHA256); md->p.ptr = ctx; md->result_size = int_sha256_len; md->block_size = int_sha256_block_len; - md->reset = int_sha256_reset; - md->update = int_sha256_update; - md->finish = int_sha256_finish; - md->free = int_sha256_free; + md->reset = int_sha2_reset; + md->update = int_sha2_update; + md->finish = int_sha2_finish; + md->free = int_sha2_free; md->reset(md); } @@ -276,18 +171,17 @@ init_sha256(PX_MD *md) void init_sha384(PX_MD *md) { - pg_sha384_ctx *ctx; - - ctx = palloc0(sizeof(*ctx)); + pg_sha2_ctx *ctx; + ctx = pg_sha2_create(PG_SHA384); md->p.ptr = ctx; md->result_size = int_sha384_len; md->block_size = int_sha384_block_len; - md->reset = int_sha384_reset; - md->update = int_sha384_update; - md->finish = int_sha384_finish; - md->free = int_sha384_free; + md->reset = int_sha2_reset; + md->update = int_sha2_update; + md->finish = int_sha2_finish; + md->free = int_sha2_free; md->reset(md); } @@ -295,18 +189,17 @@ init_sha384(PX_MD *md) void init_sha512(PX_MD *md) { - pg_sha512_ctx *ctx; - - ctx = palloc0(sizeof(*ctx)); + pg_sha2_ctx *ctx; + ctx = pg_sha2_create(PG_SHA512); md->p.ptr = ctx; md->result_size = int_sha512_len; md->block_size = int_sha512_block_len; - md->reset = int_sha512_reset; - md->update = int_sha512_update; - md->finish = int_sha512_finish; - md->free = int_sha512_free; + md->reset = int_sha2_reset; + md->update = int_sha2_update; + md->finish = int_sha2_finish; + md->free = int_sha2_free; md->reset(md); } -- 2.28.0
From 3ca6db643e3eab73e948d2ef932d1811f612695f Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Wed, 14 Oct 2020 11:38:51 +0900 Subject: [PATCH 2/2] Switch sha2_openssl.c to use EVP Postgres is two decades late for this switch. --- src/common/sha2_openssl.c | 191 +++++++++++++++++++++++-------- src/tools/pgindent/typedefs.list | 1 + 2 files changed, 143 insertions(+), 49 deletions(-) diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c index 57de96df90..3ccfc3a22e 100644 --- a/src/common/sha2_openssl.c +++ b/src/common/sha2_openssl.c @@ -20,9 +20,13 @@ #include "postgres_fe.h" #endif -#include <openssl/sha.h> +#include <openssl/evp.h> #include "common/sha2.h" +#ifndef FRONTEND +#include "utils/memutils.h" +#include "utils/resowner.h" +#endif /* * In backend, use palloc/pfree to ease the error handling. In frontend, @@ -36,6 +40,68 @@ #define FREE(ptr) free(ptr) #endif +/* + * As the allocations of the EVP context data happens within the allocator + * used by OpenSSL, use resource owner callbacks to free them on abort with + * a linked list of items saved in TopMemoryContext. + */ +#ifndef FRONTEND +typedef struct EVPContext +{ + EVP_MD_CTX *md_ctx; + + ResourceOwner owner; + struct EVPContext *next; + struct EVPContext *prev; +} EVPContext; + +static EVPContext *open_evp_contexts = NULL; +static bool evp_resowner_callback_registered = false; + +/* + * Utility wrapper to clean up an opened EVP context. + */ +static void +free_evp_context(EVPContext *evp_ctx) +{ + EVP_MD_CTX_destroy(evp_ctx->md_ctx); + if (evp_ctx->prev) + evp_ctx->prev->next = evp_ctx->next; + else + open_evp_contexts = evp_ctx->next; + if (evp_ctx->next) + evp_ctx->next->prev = evp_ctx->prev; + pfree(evp_ctx); +} + +static void +evp_context_free_callback(ResourceReleasePhase phase, + bool isCommit, + bool isTopLevel, + void *arg) +{ + EVPContext *curr; + EVPContext *next; + + if (phase != RESOURCE_RELEASE_AFTER_LOCKS) + return; + + next = open_evp_contexts; + while (next) + { + curr = next; + next = curr->next; + + if (curr->owner == CurrentResourceOwner) + { + if (isCommit) + elog(WARNING, "evp context reference leak: context %p still referenced", curr); + free_evp_context(curr); + } + } +} +#endif + /* * pg_sha2_create * @@ -45,6 +111,9 @@ pg_sha2_ctx * pg_sha2_create(pg_sha2_type type) { pg_sha2_ctx *ctx; +#ifndef FRONTEND + EVPContext *evp_ctx; +#endif ctx = ALLOC(sizeof(pg_sha2_ctx)); if (ctx == NULL) @@ -52,25 +121,47 @@ pg_sha2_create(pg_sha2_type type) ctx->type = type; - switch (type) +#ifndef FRONTEND + + /* + * Keep track of any opened EVP context data allocated in OpenSSL to avoid + * any leaks. The EVP data is built before being assigned to the list, so + * the order is important here. + */ + if (!evp_resowner_callback_registered) { - case PG_SHA224: - case PG_SHA256: - ctx->data = ALLOC(sizeof(SHA256_CTX)); - break; - case PG_SHA384: - case PG_SHA512: - ctx->data = ALLOC(sizeof(SHA512_CTX)); - break; + RegisterResourceReleaseCallback(evp_context_free_callback, NULL); + evp_resowner_callback_registered = true; } + evp_ctx = MemoryContextAlloc(TopMemoryContext, sizeof(EVPContext)); +#endif + + /* + * Initialization takes care of assigning the correct type for OpenSSL. + */ + ctx->data = EVP_MD_CTX_create(); + if (ctx->data == NULL) { +#ifndef FRONTEND + FREE(evp_ctx); + elog(ERROR, "out of memory"); +#else explicit_bzero(ctx, sizeof(pg_sha2_ctx)); FREE(ctx); return NULL; +#endif } +#ifndef FRONTEND + evp_ctx->md_ctx = ctx->data; + evp_ctx->owner = CurrentResourceOwner; + evp_ctx->next = open_evp_contexts; + evp_ctx->prev = NULL; + open_evp_contexts = evp_ctx; +#endif + return ctx; } @@ -80,7 +171,7 @@ pg_sha2_create(pg_sha2_type type) * Initialize a SHA2 context. Returns 0 on success, and -1 on failure. */ int -pg_sha2_init(pg_sha2_ctx *ctx) +pg_sha2_init(pg_sha2_ctx * ctx) { int status = 0; @@ -90,16 +181,20 @@ pg_sha2_init(pg_sha2_ctx *ctx) switch (ctx->type) { case PG_SHA224: - status = SHA224_Init((SHA256_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha224(), NULL); break; case PG_SHA256: - status = SHA256_Init((SHA256_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha256(), NULL); break; case PG_SHA384: - status = SHA384_Init((SHA512_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha384(), NULL); break; case PG_SHA512: - status = SHA512_Init((SHA512_CTX *) ctx->data); + status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha512(), NULL); break; } @@ -115,28 +210,14 @@ pg_sha2_init(pg_sha2_ctx *ctx) * Update a SHA2 context. Returns 0 on success, and -1 on failure. */ int -pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len) +pg_sha2_update(pg_sha2_ctx * ctx, const uint8 *data, size_t len) { int status; if (ctx == NULL) return 0; - switch (ctx->type) - { - case PG_SHA224: - status = SHA224_Update((SHA256_CTX *) ctx->data, data, len); - break; - case PG_SHA256: - status = SHA256_Update((SHA256_CTX *) ctx->data, data, len); - break; - case PG_SHA384: - status = SHA384_Update((SHA512_CTX *) ctx->data, data, len); - break; - case PG_SHA512: - status = SHA512_Update((SHA512_CTX *) ctx->data, data, len); - break; - } + status = EVP_DigestUpdate((EVP_MD_CTX *) ctx->data, data, len); /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) @@ -150,28 +231,14 @@ pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len) * Finalize a SHA2 context. Returns 0 on success, and -1 on failure. */ int -pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest) +pg_sha2_final(pg_sha2_ctx * ctx, uint8 *dest) { int status; if (ctx == NULL) return 0; - switch (ctx->type) - { - case PG_SHA224: - status = SHA224_Final(dest, (SHA256_CTX *) ctx->data); - break; - case PG_SHA256: - status = SHA256_Final(dest, (SHA256_CTX *) ctx->data); - break; - case PG_SHA384: - status = SHA384_Final(dest, (SHA512_CTX *) ctx->data); - break; - case PG_SHA512: - status = SHA512_Final(dest, (SHA512_CTX *) ctx->data); - break; - } + status = EVP_DigestFinal_ex((EVP_MD_CTX *) ctx->data, dest, 0); /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) @@ -185,11 +252,37 @@ pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest) * Free a SHA2 context. */ void -pg_sha2_free(pg_sha2_ctx *ctx) +pg_sha2_free(pg_sha2_ctx * ctx) { +#ifndef FRONTEND + EVPContext *curr; + EVPContext *next; +#endif + if (ctx == NULL) return; - FREE(ctx->data); + +#ifndef FRONTEND + + /* + * Look at the list of opened EVP contexts for the one to free. + */ + next = open_evp_contexts; + while (next) + { + curr = next; + next = curr->next; + + if (curr->md_ctx == ctx->data) + { + free_evp_context(curr); + break; + } + } +#else + EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data); +#endif + explicit_bzero(ctx, sizeof(pg_sha2_ctx)); FREE(ctx); } diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index c52f20d4ba..5773aaa1f9 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -569,6 +569,7 @@ EVP_CIPHER_CTX EVP_MD EVP_MD_CTX EVP_PKEY +EVPContext EachState Edge EditableObjectType -- 2.28.0
signature.asc
Description: PGP signature