Move SHA2 routines to a new generic API layer for crypto hashes
authorMichael Paquier <michael@paquier.xyz>
Wed, 2 Dec 2020 01:37:20 +0000 (10:37 +0900)
committerMichael Paquier <michael@paquier.xyz>
Wed, 2 Dec 2020 01:37:20 +0000 (10:37 +0900)
Two new routines to allocate a hash context and to free it are created,
as these become necessary for the goal behind this refactoring: switch
the all cryptohash implementations for OpenSSL to use EVP (for FIPS and
also because upstream does not recommend the use of low-level cryptohash
functions for 20 years).  Note that OpenSSL hides the internals of
cryptohash contexts since 1.1.0, so it is necessary to leave the
allocation to OpenSSL itself, explaining the need for those two new
routines.  This part is going to require more work to properly track
hash contexts with resource owners, but this not introduced here.
Still, this refactoring makes the move possible.

This reduces the number of routines for all SHA2 implementations from
twelve (SHA{224,256,386,512} with init, update and final calls) to five
(create, free, init, update and final calls) by incorporating the hash
type directly into the hash context data.

The new cryptohash routines are moved to a new file, called cryptohash.c
for the fallback implementations, with SHA2 specifics becoming a part
internal to src/common/.  OpenSSL specifics are part of
cryptohash_openssl.c.  This infrastructure is usable for more hash
types, like MD5 or HMAC.

Any code paths using the internal SHA2 routines are adapted to report
correctly errors, which are most of the changes of this commit.  The
zones mostly impacted are checksum manifests, libpq and SCRAM.

Note that e21cbb4 was a first attempt to switch SHA2 to EVP, but it
lacked the refactoring needed for libpq, as done here.

This patch has been tested on Linux and Windows, with and without
OpenSSL, and down to 1.0.1, the oldest version supported on HEAD.

Author: Michael Paquier
Reviewed-by: Daniel Gustafsson
Discussion: https://postgr.es/m/20200924025314.GE7405@paquier.xyz

23 files changed:
contrib/pgcrypto/internal-sha2.c
src/backend/libpq/auth-scram.c
src/backend/replication/backup_manifest.c
src/backend/replication/basebackup.c
src/backend/utils/adt/cryptohashes.c
src/bin/pg_verifybackup/parse_manifest.c
src/bin/pg_verifybackup/pg_verifybackup.c
src/common/Makefile
src/common/checksum_helper.c
src/common/cryptohash.c [new file with mode: 0644]
src/common/cryptohash_openssl.c [new file with mode: 0644]
src/common/scram-common.c
src/common/sha2.c
src/common/sha2_int.h [new file with mode: 0644]
src/common/sha2_openssl.c [deleted file]
src/include/common/checksum_helper.h
src/include/common/cryptohash.h [new file with mode: 0644]
src/include/common/scram-common.h
src/include/common/sha2.h
src/include/replication/backup_manifest.h
src/interfaces/libpq/fe-auth-scram.c
src/tools/msvc/Mkvcbuild.pm
src/tools/pgindent/typedefs.list

index 9fa940b5bbbbb94fc6cd368a637e170a50387c40..0fe53e15afabb9378ecba9c4c17201c731dcf638 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <time.h>
 
+#include "common/cryptohash.h"
 #include "common/sha2.h"
 #include "px.h"
 
@@ -42,7 +43,6 @@ void          init_sha384(PX_MD *h);
 void           init_sha512(PX_MD *h);
 
 /* SHA224 */
-
 static unsigned
 int_sha224_len(PX_MD *h)
 {
@@ -55,42 +55,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 +68,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 +81,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 +94,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_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-       pg_sha512_update(ctx, data, dlen);
+       if (pg_cryptohash_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_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-       pg_sha512_init(ctx);
+       if (pg_cryptohash_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_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-       pg_sha512_final(ctx, dst);
+       if (pg_cryptohash_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_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-       px_memset(ctx, 0, sizeof(*ctx));
-       pfree(ctx);
+       pg_cryptohash_free(ctx);
        pfree(h);
 }
 
@@ -238,18 +136,17 @@ int_sha512_free(PX_MD *h)
 void
 init_sha224(PX_MD *md)
 {
-       pg_sha224_ctx *ctx;
-
-       ctx = palloc0(sizeof(*ctx));
+       pg_cryptohash_ctx *ctx;
 
+       ctx = pg_cryptohash_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 +154,17 @@ init_sha224(PX_MD *md)
 void
 init_sha256(PX_MD *md)
 {
-       pg_sha256_ctx *ctx;
-
-       ctx = palloc0(sizeof(*ctx));
+       pg_cryptohash_ctx *ctx;
 
+       ctx = pg_cryptohash_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 +172,17 @@ init_sha256(PX_MD *md)
 void
 init_sha384(PX_MD *md)
 {
-       pg_sha384_ctx *ctx;
-
-       ctx = palloc0(sizeof(*ctx));
+       pg_cryptohash_ctx *ctx;
 
+       ctx = pg_cryptohash_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 +190,17 @@ init_sha384(PX_MD *md)
 void
 init_sha512(PX_MD *md)
 {
-       pg_sha512_ctx *ctx;
-
-       ctx = palloc0(sizeof(*ctx));
+       pg_cryptohash_ctx *ctx;
 
+       ctx = pg_cryptohash_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);
 }
index 0f79b28bb5ab3e97f9b9bdb2a4ebb8eed9a6b316..6879a8161832629df1ffe452924f674766e156e4 100644 (file)
@@ -527,8 +527,12 @@ 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);
@@ -651,8 +655,17 @@ mock_scram_secret(const char *username, int *iterations, char **salt,
        char       *encoded_salt;
        int                     encoded_len;
 
-       /* Generate deterministic salt */
+       /*
+        * Generate deterministic salt.
+        *
+        * Note that we cannot reveal any information to an attacker here so the
+        * error messages need to remain generic.  This should never fail anyway
+        * as the salt generated for mock authentication uses the cluster's nonce
+        * value.
+        */
        raw_salt = scram_mock_salt(username);
+       if (raw_salt == NULL)
+               elog(ERROR, "could not encode salt");
 
        encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN);
        /* don't forget the zero-terminator */
@@ -660,12 +673,6 @@ mock_scram_secret(const char *username, int *iterations, char **salt,
        encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt,
                                                                encoded_len);
 
-       /*
-        * Note that we cannot reveal any information to an attacker here so the
-        * error message needs to remain generic.  This should never fail anyway
-        * as the salt generated for mock authentication uses the cluster's nonce
-        * value.
-        */
        if (encoded_len < 0)
                elog(ERROR, "could not encode salt");
        encoded_salt[encoded_len] = '\0';
@@ -1084,7 +1091,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 +1103,35 @@ 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 +1362,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 +1407,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_cryptohash_ctx *ctx;
        static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
        char       *mock_auth_nonce = GetMockAuthenticationNonce();
 
@@ -1406,10 +1425,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_cryptohash_create(PG_SHA256);
+       if (pg_cryptohash_init(ctx) < 0 ||
+               pg_cryptohash_update(ctx, (uint8 *) username, strlen(username)) < 0 ||
+               pg_cryptohash_update(ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN) < 0 ||
+               pg_cryptohash_final(ctx, sha_digest) < 0)
+       {
+               pg_cryptohash_free(ctx);
+               return NULL;
+       }
+       pg_cryptohash_free(ctx);
 
        return (char *) sha_digest;
 }
index bab5e2f53b08382652dfd3e64b8c50dd0b5c4b3a..c3f339c55631d0082311c2948bf34521f3835fc3 100644 (file)
@@ -65,7 +65,9 @@ InitializeBackupManifest(backup_manifest_info *manifest,
        else
        {
                manifest->buffile = BufFileCreateTemp(false);
-               pg_sha256_init(&manifest->manifest_ctx);
+               manifest->manifest_ctx = pg_cryptohash_create(PG_SHA256);
+               if (pg_cryptohash_init(manifest->manifest_ctx) < 0)
+                       elog(ERROR, "failed to initialize checksum of backup manifest");
        }
 
        manifest->manifest_size = UINT64CONST(0);
@@ -79,6 +81,16 @@ InitializeBackupManifest(backup_manifest_info *manifest,
                                                 "\"Files\": [");
 }
 
+/*
+ * Free resources assigned to a backup manifest constructed.
+ */
+void
+FreeBackupManifest(backup_manifest_info *manifest)
+{
+       pg_cryptohash_free(manifest->manifest_ctx);
+       manifest->manifest_ctx = NULL;
+}
+
 /*
  * Add an entry to the backup manifest for a file.
  */
@@ -166,6 +178,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\": \"",
@@ -310,7 +325,8 @@ SendBackupManifest(backup_manifest_info *manifest)
         * twice.
         */
        manifest->still_checksumming = false;
-       pg_sha256_final(&manifest->manifest_ctx, checksumbuf);
+       if (pg_cryptohash_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';
@@ -373,7 +389,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_cryptohash_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;
 }
index b89df01fa76fb7029b2310d490839de8f1ae8722..22be7ca9d5fceaec6673076d0a6c4807a4f9458b 100644 (file)
@@ -733,6 +733,7 @@ perform_base_backup(basebackup_options *opt)
        WalSndResourceCleanup(true);
 
        pgstat_progress_end_command();
+       FreeBackupManifest(&manifest);
 }
 
 /*
@@ -1094,7 +1095,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 +1133,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 +1590,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 +1766,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 +1781,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 +1790,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);
index e897660927ff415e04cbcd3a5aa0d6e2e55d7458..5de294a7fda5611acd7ad4d18b9fb61b362424c2 100644 (file)
@@ -13,6 +13,7 @@
  */
 #include "postgres.h"
 
+#include "common/cryptohash.h"
 #include "common/md5.h"
 #include "common/sha2.h"
 #include "utils/builtins.h"
@@ -78,16 +79,21 @@ sha224_bytea(PG_FUNCTION_ARGS)
        bytea      *in = PG_GETARG_BYTEA_PP(0);
        const uint8 *data;
        size_t          len;
-       pg_sha224_ctx ctx;
+       pg_cryptohash_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_cryptohash_create(PG_SHA224);
+       if (pg_cryptohash_init(ctx) < 0)
+               elog(ERROR, "could not initialize %s context", "SHA224");
+       if (pg_cryptohash_update(ctx, data, len) < 0)
+               elog(ERROR, "could not update %s context", "SHA224");
+       if (pg_cryptohash_final(ctx, buf) < 0)
+               elog(ERROR, "could not finalize %s context", "SHA224");
+       pg_cryptohash_free(ctx);
 
        result = palloc(sizeof(buf) + VARHDRSZ);
        SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@@ -102,16 +108,21 @@ sha256_bytea(PG_FUNCTION_ARGS)
        bytea      *in = PG_GETARG_BYTEA_PP(0);
        const uint8 *data;
        size_t          len;
-       pg_sha256_ctx ctx;
+       pg_cryptohash_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_cryptohash_create(PG_SHA256);
+       if (pg_cryptohash_init(ctx) < 0)
+               elog(ERROR, "could not initialize %s context", "SHA256");
+       if (pg_cryptohash_update(ctx, data, len) < 0)
+               elog(ERROR, "could not update %s context", "SHA256");
+       if (pg_cryptohash_final(ctx, buf) < 0)
+               elog(ERROR, "could not finalize %s context", "SHA256");
+       pg_cryptohash_free(ctx);
 
        result = palloc(sizeof(buf) + VARHDRSZ);
        SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@@ -126,16 +137,21 @@ sha384_bytea(PG_FUNCTION_ARGS)
        bytea      *in = PG_GETARG_BYTEA_PP(0);
        const uint8 *data;
        size_t          len;
-       pg_sha384_ctx ctx;
+       pg_cryptohash_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_cryptohash_create(PG_SHA384);
+       if (pg_cryptohash_init(ctx) < 0)
+               elog(ERROR, "could not initialize %s context", "SHA384");
+       if (pg_cryptohash_update(ctx, data, len) < 0)
+               elog(ERROR, "could not update %s context", "SHA384");
+       if (pg_cryptohash_final(ctx, buf) < 0)
+               elog(ERROR, "could not finalize %s context", "SHA384");
+       pg_cryptohash_free(ctx);
 
        result = palloc(sizeof(buf) + VARHDRSZ);
        SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@@ -150,16 +166,21 @@ sha512_bytea(PG_FUNCTION_ARGS)
        bytea      *in = PG_GETARG_BYTEA_PP(0);
        const uint8 *data;
        size_t          len;
-       pg_sha512_ctx ctx;
+       pg_cryptohash_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_cryptohash_create(PG_SHA512);
+       if (pg_cryptohash_init(ctx) < 0)
+               elog(ERROR, "could not initialize %s context", "SHA512");
+       if (pg_cryptohash_update(ctx, data, len) < 0)
+               elog(ERROR, "could not update %s context", "SHA512");
+       if (pg_cryptohash_final(ctx, buf) < 0)
+               elog(ERROR, "could not finalize %s context", "SHA512");
+       pg_cryptohash_free(ctx);
 
        result = palloc(sizeof(buf) + VARHDRSZ);
        SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
index 608e23538bad26c139a80a7375739e8e8641c24b..5b4ce2883714940e86ab389c408dc1dfcca685f3 100644 (file)
@@ -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_cryptohash_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_cryptohash_create(PG_SHA256);
+       if (manifest_ctx == NULL)
+               context->error_cb(context, "out of memory");
+       if (pg_cryptohash_init(manifest_ctx) < 0)
+               context->error_cb(context, "could not initialize checksum of manifest");
+       if (pg_cryptohash_update(manifest_ctx, (uint8 *) buffer, penultimate_newline + 1) < 0)
+               context->error_cb(context, "could not update checksum of manifest");
+       if (pg_cryptohash_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)
@@ -667,6 +673,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
        if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
                           PG_SHA256_DIGEST_LENGTH) != 0)
                context->error_cb(context, "manifest checksum mismatch");
+       pg_cryptohash_free(manifest_ctx);
 }
 
 /*
index bb3733b57e20433d76be9bd659cda88559411409..07320d369975892b290ec8d50043860d389fc42b 100644 (file)
@@ -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)
index 25c55bd6423c3fd890b4cc99528eec5940494d75..b8f5187282611fdcc999407ef583a12542219a82 100644 (file)
@@ -82,9 +82,11 @@ OBJS_COMMON = \
 ifeq ($(with_openssl),yes)
 OBJS_COMMON += \
        protocol_openssl.o \
-       sha2_openssl.o
+       cryptohash_openssl.o
 else
-OBJS_COMMON += sha2.o
+OBJS_COMMON += \
+       cryptohash.o \
+       sha2.o
 endif
 
 # A few files are currently only built for frontend, not server
index 79a9a7447b3f13f7881e78b72789477a01516d23..8e06524cd3c7a44c4e1c5680292ecc83efcfd953 100644 (file)
@@ -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_cryptohash_create(PG_SHA224);
+                       if (context->raw_context.c_sha224 == NULL)
+                               return -1;
+                       if (pg_cryptohash_init(context->raw_context.c_sha224) < 0)
+                       {
+                               pg_cryptohash_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_cryptohash_create(PG_SHA256);
+                       if (context->raw_context.c_sha256 == NULL)
+                               return -1;
+                       if (pg_cryptohash_init(context->raw_context.c_sha256) < 0)
+                       {
+                               pg_cryptohash_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_cryptohash_create(PG_SHA384);
+                       if (context->raw_context.c_sha384 == NULL)
+                               return -1;
+                       if (pg_cryptohash_init(context->raw_context.c_sha384) < 0)
+                       {
+                               pg_cryptohash_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_cryptohash_create(PG_SHA512);
+                       if (context->raw_context.c_sha512 == NULL)
+                               return -1;
+                       if (pg_cryptohash_init(context->raw_context.c_sha512) < 0)
+                       {
+                               pg_cryptohash_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_cryptohash_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_cryptohash_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_cryptohash_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_cryptohash_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_cryptohash_final(context->raw_context.c_sha224, output) < 0)
+                               return -1;
+                       pg_cryptohash_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_cryptohash_final(context->raw_context.c_sha256, output) < 0)
+                               return -1;
+                       pg_cryptohash_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_cryptohash_final(context->raw_context.c_sha384, output) < 0)
+                               return -1;
+                       pg_cryptohash_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_cryptohash_final(context->raw_context.c_sha512, output) < 0)
+                               return -1;
+                       pg_cryptohash_free(context->raw_context.c_sha512);
                        retval = PG_SHA512_DIGEST_LENGTH;
                        break;
        }
diff --git a/src/common/cryptohash.c b/src/common/cryptohash.c
new file mode 100644 (file)
index 0000000..a61091f
--- /dev/null
@@ -0,0 +1,190 @@
+/*-------------------------------------------------------------------------
+ *
+ * cryptohash.c
+ *       Fallback implementations for cryptographic hash functions.
+ *
+ * This is the set of in-core functions used when there are no other
+ * alternative options like OpenSSL.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/common/cryptohash.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <sys/param.h>
+
+#include "common/cryptohash.h"
+#include "sha2_int.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
+
+/*
+ * pg_cryptohash_create
+ *
+ * Allocate a hash context.  Returns NULL on failure for an OOM.  The
+ * backend issues an error, without returning.
+ */
+pg_cryptohash_ctx *
+pg_cryptohash_create(pg_cryptohash_type type)
+{
+       pg_cryptohash_ctx *ctx;
+
+       ctx = ALLOC(sizeof(pg_cryptohash_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_cryptohash_ctx));
+               FREE(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+/*
+ * pg_cryptohash_init
+ *
+ * Initialize a hash context.  Note that this implementation is designed
+ * to never fail, so this always returns 0.
+ */
+int
+pg_cryptohash_init(pg_cryptohash_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_cryptohash_update
+ *
+ * Update a hash context.  Note that this implementation is designed
+ * to never fail, so this always returns 0.
+ */
+int
+pg_cryptohash_update(pg_cryptohash_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_cryptohash_final
+ *
+ * Finalize a hash context.  Note that this implementation is designed
+ * to never fail, so this always returns 0.
+ */
+int
+pg_cryptohash_final(pg_cryptohash_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_cryptohash_free
+ *
+ * Free a hash context.
+ */
+void
+pg_cryptohash_free(pg_cryptohash_ctx *ctx)
+{
+       if (ctx == NULL)
+               return;
+       FREE(ctx->data);
+       explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+       FREE(ctx);
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
new file mode 100644 (file)
index 0000000..8e2c69b
--- /dev/null
@@ -0,0 +1,197 @@
+/*-------------------------------------------------------------------------
+ *
+ * cryptohash_openssl.c
+ *       Set of wrapper routines on top of OpenSSL to support cryptographic
+ *       hash functions.
+ *
+ * This should only be used if code is compiled with OpenSSL support.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *               src/common/cryptohash_openssl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <openssl/sha.h>
+
+#include "common/cryptohash.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
+
+/*
+ * pg_cryptohash_create
+ *
+ * Allocate a hash context.  Returns NULL on failure for an OOM.  The
+ * backend issues an error, without returning.
+ */
+pg_cryptohash_ctx *
+pg_cryptohash_create(pg_cryptohash_type type)
+{
+       pg_cryptohash_ctx *ctx;
+
+       ctx = ALLOC(sizeof(pg_cryptohash_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_cryptohash_ctx));
+               FREE(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+/*
+ * pg_cryptohash_init
+ *
+ * Initialize a hash context.  Returns 0 on success, and -1 on failure.
+ */
+int
+pg_cryptohash_init(pg_cryptohash_ctx *ctx)
+{
+       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;
+}
+
+/*
+ * pg_cryptohash_update
+ *
+ * Update a hash context.  Returns 0 on success, and -1 on failure.
+ */
+int
+pg_cryptohash_update(pg_cryptohash_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;
+       }
+
+       /* OpenSSL internals return 1 on success, 0 on failure */
+       if (status <= 0)
+               return -1;
+       return 0;
+}
+
+/*
+ * pg_cryptohash_final
+ *
+ * Finalize a hash context.  Returns 0 on success, and -1 on failure.
+ */
+int
+pg_cryptohash_final(pg_cryptohash_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;
+       }
+
+       /* OpenSSL internals return 1 on success, 0 on failure */
+       if (status <= 0)
+               return -1;
+       return 0;
+}
+
+/*
+ * pg_cryptohash_free
+ *
+ * Free a hash context.
+ */
+void
+pg_cryptohash_free(pg_cryptohash_ctx *ctx)
+{
+       if (ctx == NULL)
+               return;
+       FREE(ctx->data);
+       explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+       FREE(ctx);
+}
index 4971134b22aa1155d10b310f044f2b6a02ac23a2..caab68926d2929099ed6c3c9b078740051c65f05 100644 (file)
@@ -29,9 +29,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 +44,21 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
         */
        if (keylen > SHA256_HMAC_B)
        {
-               pg_sha256_ctx sha256_ctx;
-
-               pg_sha256_init(&sha256_ctx);
-               pg_sha256_update(&sha256_ctx, key, keylen);
-               pg_sha256_final(&sha256_ctx, keybuf);
+               pg_cryptohash_ctx *sha256_ctx;
+
+               sha256_ctx = pg_cryptohash_create(PG_SHA256);
+               if (sha256_ctx == NULL)
+                       return -1;
+               if (pg_cryptohash_init(sha256_ctx) < 0 ||
+                       pg_cryptohash_update(sha256_ctx, key, keylen) < 0 ||
+                       pg_cryptohash_final(sha256_ctx, keybuf) < 0)
+               {
+                       pg_cryptohash_free(sha256_ctx);
+                       return -1;
+               }
                key = keybuf;
                keylen = SCRAM_KEY_LEN;
+               pg_cryptohash_free(sha256_ctx);
        }
 
        memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B);
@@ -62,45 +70,75 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
                ctx->k_opad[i] ^= key[i];
        }
 
+       ctx->sha256ctx = pg_cryptohash_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_cryptohash_init(ctx->sha256ctx) < 0 ||
+               pg_cryptohash_update(ctx->sha256ctx, k_ipad, SHA256_HMAC_B) < 0)
+       {
+               pg_cryptohash_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_cryptohash_update(ctx->sha256ctx, (const uint8 *) str, slen) < 0)
+       {
+               pg_cryptohash_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_cryptohash_final(ctx->sha256ctx, h) < 0)
+       {
+               pg_cryptohash_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_cryptohash_init(ctx->sha256ctx) < 0 ||
+               pg_cryptohash_update(ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B) < 0 ||
+               pg_cryptohash_update(ctx->sha256ctx, h, SCRAM_KEY_LEN) < 0 ||
+               pg_cryptohash_final(ctx->sha256ctx, result) < 0)
+       {
+               pg_cryptohash_free(ctx->sha256ctx);
+               return -1;
+       }
+
+       pg_cryptohash_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 +158,94 @@ 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_cryptohash_ctx *ctx;
 
-       pg_sha256_init(&ctx);
-       pg_sha256_update(&ctx, input, len);
-       pg_sha256_final(&ctx, result);
+       ctx = pg_cryptohash_create(PG_SHA256);
+       if (ctx == NULL)
+               return -1;
+
+       if (pg_cryptohash_init(ctx) < 0 ||
+               pg_cryptohash_update(ctx, input, len) < 0 ||
+               pg_cryptohash_final(ctx, result) < 0)
+       {
+               pg_cryptohash_free(ctx);
+               return -1;
+       }
+
+       pg_cryptohash_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 +276,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:
index 0d329bb23898e3c6f0dab35957fdb2191ef7697f..1a462accc51f811601b184d92ed6e3ab9540dc96 100644 (file)
@@ -1,12 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * sha2.c
- *       Set of SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
+ *        SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
  *
- * This is the set of in-core functions used when there are no other
- * alternative options like OpenSSL.
+ * This includes the fallback implementation for SHA2 cryptographic
+ * hashes.
  *
- * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
  *       src/common/sha2.c
 #include "postgres_fe.h"
 #endif
 
-#include <sys/param.h>
+#include "sha2_int.h"
 
-#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
 
 /*
  * UNROLLED TRANSFORM LOOP NOTE:
diff --git a/src/common/sha2_int.h b/src/common/sha2_int.h
new file mode 100644 (file)
index 0000000..96db773
--- /dev/null
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------------------------
+ *
+ * sha2_int.h
+ *       Internal headers for fallback implementation of SHA{224,256,384,512}
+ *
+ * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *               src/common/sha2_int.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */
+
+/*
+ * FILE:       sha2.h
+ * AUTHOR:     Aaron D. Gifford <me@aarongifford.com>
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ *       may be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef PG_SHA2_INT_H
+#define PG_SHA2_INT_H
+
+#include "common/sha2.h"
+
+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;
+
+/* 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);
+
+#endif                                                 /* PG_SHA2_INT_H */
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
deleted file mode 100644 (file)
index 41673b3..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sha2_openssl.c
- *       Set of wrapper routines on top of OpenSSL to support SHA-224
- *       SHA-256, SHA-384 and SHA-512 functions.
- *
- * This should only be used if code is compiled with OpenSSL support.
- *
- * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- *               src/common/sha2_openssl.c
- *
- *-------------------------------------------------------------------------
- */
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#include <openssl/sha.h>
-
-#include "common/sha2.h"
-
-
-/* Interface routines for SHA-256 */
-void
-pg_sha256_init(pg_sha256_ctx *ctx)
-{
-       SHA256_Init((SHA256_CTX *) ctx);
-}
-
-void
-pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len)
-{
-       SHA256_Update((SHA256_CTX *) ctx, data, len);
-}
-
-void
-pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest)
-{
-       SHA256_Final(dest, (SHA256_CTX *) ctx);
-}
-
-/* Interface routines for SHA-512 */
-void
-pg_sha512_init(pg_sha512_ctx *ctx)
-{
-       SHA512_Init((SHA512_CTX *) ctx);
-}
-
-void
-pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len)
-{
-       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);
-}
index 48b0745dadd655c7c58ebfe02fadf4296ee90db2..b07a34e7e49892cddc72cd1b712ecc4e640f4b2f 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef CHECKSUM_HELPER_H
 #define CHECKSUM_HELPER_H
 
+#include "common/cryptohash.h"
 #include "common/sha2.h"
 #include "port/pg_crc32c.h"
 
@@ -41,10 +42,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_cryptohash_ctx *c_sha224;
+       pg_cryptohash_ctx *c_sha256;
+       pg_cryptohash_ctx *c_sha384;
+       pg_cryptohash_ctx *c_sha512;
 } pg_checksum_raw_context;
 
 /*
@@ -66,8 +67,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/cryptohash.h b/src/include/common/cryptohash.h
new file mode 100644 (file)
index 0000000..0e4a663
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * cryptohash.h
+ *       Generic headers for cryptographic hash functions.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *               src/include/common/cryptohash.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PG_CRYPTOHASH_H
+#define PG_CRYPTOHASH_H
+
+/* Context Structures for each hash function */
+typedef enum
+{
+       PG_SHA224 = 0,
+       PG_SHA256,
+       PG_SHA384,
+       PG_SHA512
+} pg_cryptohash_type;
+
+typedef struct pg_cryptohash_ctx
+{
+       pg_cryptohash_type type;
+       /* private area used by each hash implementation */
+       void       *data;
+} pg_cryptohash_ctx;
+
+extern pg_cryptohash_ctx *pg_cryptohash_create(pg_cryptohash_type type);
+extern int     pg_cryptohash_init(pg_cryptohash_ctx *ctx);
+extern int     pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len);
+extern int     pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest);
+extern void pg_cryptohash_free(pg_cryptohash_ctx *ctx);
+
+#endif                                                 /* PG_CRYPTOHASH_H */
index 2edae2dd3c0f5e3900282c6c4cd39100fc2eedd7..f4a7c60725b312465e4ef337eb67e5094a2bff19 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef SCRAM_COMMON_H
 #define SCRAM_COMMON_H
 
+#include "common/cryptohash.h"
 #include "common/sha2.h"
 
 /* Name of SCRAM mechanisms per IANA */
  */
 typedef struct
 {
-       pg_sha256_ctx sha256ctx;
+       pg_cryptohash_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);
index 9c4abf777d4393247243076bd081db1bc72853a7..c8b9096043ff3bfe7e5b01ea9b93b219e9f6b9ca 100644 (file)
@@ -1,9 +1,10 @@
 /*-------------------------------------------------------------------------
  *
  * sha2.h
- *       Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
+ *       Constants related to SHA224, 256, 384 AND 512.
  *
- * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
  *               src/include/common/sha2.h
  *-------------------------------------------------------------------------
  */
 
-/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */
-
-/*
- * FILE:       sha2.h
- * AUTHOR:     Aaron D. Gifford <me@aarongifford.com>
- *
- * Copyright (c) 2000-2001, Aaron D. Gifford
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holder nor the names of contributors
- *       may be used to endorse or promote products derived from this software
- *       without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
- */
-
 #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
 #define PG_SHA512_DIGEST_LENGTH                        64
 #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
-{
-       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;
-#endif                                                 /* USE_OPENSSL */
-
-/* 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);
-
 #endif                                                 /* _PG_SHA2_H_ */
index fb1291cbe4d4aa3f0a967237518a2f53964a008e..e7c40474972f45d5640fab6c96e177772549fdb1 100644 (file)
@@ -28,7 +28,7 @@ typedef struct backup_manifest_info
 {
        BufFile    *buffile;
        pg_checksum_type checksum_type;
-       pg_sha256_ctx manifest_ctx;
+       pg_cryptohash_ctx *manifest_ctx;
        uint64          manifest_size;
        bool            force_encode;
        bool            first_file;
@@ -48,5 +48,6 @@ extern void AddWALInfoToBackupManifest(backup_manifest_info *manifest,
                                                                           TimeLineID starttli, XLogRecPtr endptr,
                                                                           TimeLineID endtli);
 extern void SendBackupManifest(backup_manifest_info *manifest);
+extern void FreeBackupManifest(backup_manifest_info *manifest);
 
 #endif
index 6d266e97965520d2b5f025149b04b5b85e7914d5..6dcf574f62f3b8c5ae20ceb0522bceb89a8b9622 100644 (file)
@@ -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,60 +772,70 @@ 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);
+               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)
-               return false;
+               *match = false;
+       else
+               *match = true;
 
        return true;
 }
index 90594bd41bac9c9956f47dd009f34aa9858082a5..720b55142b3ebc704266358659a3f38f17ddf0af 100644 (file)
@@ -129,11 +129,12 @@ sub mkvcbuild
 
        if ($solution->{options}->{openssl})
        {
-               push(@pgcommonallfiles, 'sha2_openssl.c');
+               push(@pgcommonallfiles, 'cryptohash_openssl.c');
                push(@pgcommonallfiles, 'protocol_openssl.c');
        }
        else
        {
+               push(@pgcommonallfiles, 'cryptohash.c');
                push(@pgcommonallfiles, 'sha2.c');
        }
 
index b8ca8cffd9198ad5f07c35140b98adce2ef6d8c6..04464c2e768ccc39b38f76f85b74259f1d0a46de 100644 (file)
@@ -3179,6 +3179,8 @@ pg_conn_host_type
 pg_conv_map
 pg_crc32
 pg_crc32c
+pg_cryptohash_ctx
+pg_cryptohash_type
 pg_ctype_cache
 pg_enc
 pg_enc2gettext