scram-sha-256 broken with FIPS and OpenSSL 1.0.2
Hi all,
Enabling FIPS with OpenSSL 1.0.2 causes direct calls to the SHAXXX
routines to fail:
"Low level API call to digest SHA256 forbidden in fips mode"
This got discussed back in 2018, but I never got back to it:
/messages/by-id/20180911030250.GA27115@paquier.xyz
One thing I did not like back in the past patch was that we did not
handle failures if one of OpenSSL's call failed, but this can easily
be handled by using a trick similar to jsonapi.c to fail hard if that
happens.
It is worth noting that the low-level SHA routines are not recommended
for years in OpenSSL, and that these have been officially marked as
deprecated in 3.0.0. So, while the changes in sha2.h don't make this
stuff back-patchable per the ABI breakage it introduces, switching
sha2_openssl.c to use EVP is a better move in the long term, even if
that means that SCRAM+FIPS would not work with PG 10~13, so the
attached is something for HEAD, even if this would be possible to do
in older releases as the routines used in the attached are available
in versions of OpenSSL older than 1.0.1.
Any thoughts?
--
Michael
Attachments:
sha2-evp-v1.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index 9c4abf777d..2c52838161 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -51,7 +51,7 @@
#define _PG_SHA2_H_
#ifdef USE_OPENSSL
-#include <openssl/sha.h>
+#include <openssl/evp.h>
#endif
/*** SHA224/256/384/512 Various Length Definitions ***********************/
@@ -70,10 +70,10 @@
/* 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;
+typedef EVP_MD_CTX *pg_sha256_ctx;
+typedef EVP_MD_CTX *pg_sha512_ctx;
+typedef EVP_MD_CTX *pg_sha224_ctx;
+typedef EVP_MD_CTX *pg_sha384_ctx;
#else
typedef struct pg_sha256_ctx
{
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
index 41673b3a88..1d0b254487 100644
--- a/src/common/sha2_openssl.c
+++ b/src/common/sha2_openssl.c
@@ -20,83 +20,116 @@
#include "postgres_fe.h"
#endif
-#include <openssl/sha.h>
-
#include "common/sha2.h"
+#ifdef FRONTEND
+#include "common/logging.h"
+#else
+#include "miscadmin.h"
+#endif
+
+#ifdef FRONTEND
+#define sha2_log_and_abort(...) \
+ do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+#else
+#define sha2_log_and_abort(...) elog(ERROR, __VA_ARGS__)
+#endif
+
+static void
+digest_init(EVP_MD_CTX **ctx, const EVP_MD *type)
+{
+ *ctx = EVP_MD_CTX_create();
+ if (*ctx == NULL)
+ sha2_log_and_abort("could not create EVP digest context");
+ EVP_DigestInit_ex(*ctx, type, NULL);
+}
+
+static void
+digest_update(EVP_MD_CTX **ctx, const uint8 *data, size_t len)
+{
+ EVP_DigestUpdate(*ctx, data, len);
+}
+
+static void
+digest_final(EVP_MD_CTX **ctx, uint8 *dest)
+{
+ if (EVP_DigestFinal_ex(*ctx, dest, 0) <= 0)
+ sha2_log_and_abort("could not finalize EVP digest context");
+ EVP_MD_CTX_destroy(*ctx);
+}
/* Interface routines for SHA-256 */
void
pg_sha256_init(pg_sha256_ctx *ctx)
{
- SHA256_Init((SHA256_CTX *) ctx);
+ digest_init(ctx, EVP_sha256());
}
void
pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len)
{
- SHA256_Update((SHA256_CTX *) ctx, data, len);
+ digest_update(ctx, data, len);
}
void
pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest)
{
- SHA256_Final(dest, (SHA256_CTX *) ctx);
+ digest_final(ctx, dest);
}
/* Interface routines for SHA-512 */
void
pg_sha512_init(pg_sha512_ctx *ctx)
{
- SHA512_Init((SHA512_CTX *) ctx);
+ digest_init(ctx, EVP_sha512());
}
void
pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len)
{
- SHA512_Update((SHA512_CTX *) ctx, data, len);
+ digest_update(ctx, data, len);
}
void
pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest)
{
- SHA512_Final(dest, (SHA512_CTX *) ctx);
+ digest_final(ctx, dest);
}
/* Interface routines for SHA-384 */
void
pg_sha384_init(pg_sha384_ctx *ctx)
{
- SHA384_Init((SHA512_CTX *) ctx);
+ digest_init(ctx, EVP_sha384());
}
void
pg_sha384_update(pg_sha384_ctx *ctx, const uint8 *data, size_t len)
{
- SHA384_Update((SHA512_CTX *) ctx, data, len);
+ digest_update(ctx, data, len);
}
void
pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest)
{
- SHA384_Final(dest, (SHA512_CTX *) ctx);
+ digest_final(ctx, dest);
}
/* Interface routines for SHA-224 */
void
pg_sha224_init(pg_sha224_ctx *ctx)
{
- SHA224_Init((SHA256_CTX *) ctx);
+ digest_init(ctx, EVP_sha224());
}
void
pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *data, size_t len)
{
- SHA224_Update((SHA256_CTX *) ctx, data, len);
+ digest_update(ctx, data, len);
}
void
pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest)
{
- SHA224_Final(dest, (SHA256_CTX *) ctx);
+ digest_final(ctx, dest);
}
On 24 Sep 2020, at 04:53, Michael Paquier <michael@paquier.xyz> wrote:
Enabling FIPS with OpenSSL 1.0.2 causes direct calls to the SHAXXX
routines to fail:
"Low level API call to digest SHA256 forbidden in fips mode"
Confirmed by running 1.0.2s-fips with fips_mode=yes in the alg_section in
openssl.cnf.
This got discussed back in 2018, but I never got back to it:
/messages/by-id/20180911030250.GA27115@paquier.xyzOne thing I did not like back in the past patch was that we did not
handle failures if one of OpenSSL's call failed, but this can easily
be handled by using a trick similar to jsonapi.c to fail hard if that
happens.It is worth noting that the low-level SHA routines are not recommended
for years in OpenSSL, and that these have been officially marked as
deprecated in 3.0.0. So, while the changes in sha2.h don't make this
stuff back-patchable per the ABI breakage it introduces, switching
sha2_openssl.c to use EVP is a better move in the long term,
Agreed. Commit 5ff4a67f63f [0]/messages/by-id/561274F1.1030000@iki.fi moved contrib/pgcrypto from using low-level
functions for pretty much exactly the same reasons: they are advised against
and break FIPS.
With your patch I can run the SSL tests successfully both with and without
FIPS. I'm in favor of moving to the EVP API.
..even if
that means that SCRAM+FIPS would not work with PG 10~13, so the
attached is something for HEAD, even if this would be possible to do
in older releases as the routines used in the attached are available
in versions of OpenSSL older than 1.0.1.
Thats kind of a shame, but given the low volume of reports to -bugs and
-hackers, the combination SCRAM and FIPS might not be all too common. Since
OpenSSL FIPS is EOL it might also not increase until 3.0.0 comes with FIPS
certification?
If we really want to support it (which would require more evidence of it being
a problem IMO), using the non-OpenSSL sha256 code would be one option I guess?
cheers ./daniel
On 24/09/2020 17:21, Daniel Gustafsson wrote:
If we really want to support it (which would require more evidence of it being
a problem IMO), using the non-OpenSSL sha256 code would be one option I guess?
That would technically work, but wouldn't it make the product as whole
not FIPS compliant? I'm not a FIPS lawyer, but as I understand it the
point of FIPS is that all the crypto code is encapsulated in a certified
module. Having your own SHA-256 implementation would defeat that.
- Heikki
On 24 Sep 2020, at 18:21, Heikki Linnakangas <hlinnaka@iki.fi> wrote:
On 24/09/2020 17:21, Daniel Gustafsson wrote:
If we really want to support it (which would require more evidence of it being
a problem IMO), using the non-OpenSSL sha256 code would be one option I guess?That would technically work, but wouldn't it make the product as whole not FIPS compliant? I'm not a FIPS lawyer, but as I understand it the point of FIPS is that all the crypto code is encapsulated in a certified module. Having your own SHA-256 implementation would defeat that.
Doh, of course, I blame a lack of caffeine this afternoon. Having a private
local sha256 implementation using the EVP_* API inside scram-common would
maintain FIPS compliance and ABI compatibility, but would also be rather ugly.
cheers ./daniel
On 2020-09-24 18:21, Heikki Linnakangas wrote:
That would technically work, but wouldn't it make the product as whole
not FIPS compliant? I'm not a FIPS lawyer, but as I understand it the
point of FIPS is that all the crypto code is encapsulated in a certified
module. Having your own SHA-256 implementation would defeat that.
Depends on what one considers to be covered by FIPS. The entire rest of
SCRAM is custom code, so running it on top of the world's greatest
SHA-256 implementation isn't going to make the end product any more
trustworthy.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Thu, Sep 24, 2020 at 1:57 PM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
Depends on what one considers to be covered by FIPS. The entire rest of
SCRAM is custom code, so running it on top of the world's greatest
SHA-256 implementation isn't going to make the end product any more
trustworthy.
I mean, the issue here, as is so often the case, is not what is
actually more secure, but what meets the terms of some security
standard. At least in the US, FIPS 140-2 compliance is a reasonably
common need, so if we can make it easier for people who have that need
to be compliant, they are more likely to use PostgreSQL, which seems
like something that we should want. Our opinions about that standard
do not matter to the users who are legally required to comply with it;
the opinions of their lawyers and auditors do.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 24 Sep 2020, at 21:22, Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Sep 24, 2020 at 1:57 PM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:Depends on what one considers to be covered by FIPS. The entire rest of
SCRAM is custom code, so running it on top of the world's greatest
SHA-256 implementation isn't going to make the end product any more
trustworthy.I mean, the issue here, as is so often the case, is not what is
actually more secure, but what meets the terms of some security
standard.
Correct, IIUC in order to be FIPS compliant all cryptographic modules used must
be FIPS certified.
At least in the US, FIPS 140-2 compliance is a reasonably
common need, so if we can make it easier for people who have that need
to be compliant, they are more likely to use PostgreSQL, which seems
like something that we should want.
The proposed patch makes SCRAM+FIPS work for 14, question is if we need/want to
try and address v10-13.
cheers ./daniel
On Thu, Sep 24, 2020 at 06:28:25PM +0200, Daniel Gustafsson wrote:
Doh, of course, I blame a lack of caffeine this afternoon. Having a private
local sha256 implementation using the EVP_* API inside scram-common would
maintain FIPS compliance and ABI compatibility, but would also be rather ugly.
Even if we'd try to force our internal implementation of SHA256 on
already-released branches instead of the one of OpenSSL, this would be
an ABI break for compiled modules expected to work on this released
branch as OpenSSL's internal SHA structures don't exactly match with
our own implementation (think just about sizeof() or such).
--
Michael
On Thu, Sep 24, 2020 at 09:44:57PM +0200, Daniel Gustafsson wrote:
On 24 Sep 2020, at 21:22, Robert Haas <robertmhaas@gmail.com> wrote:
I mean, the issue here, as is so often the case, is not what is
actually more secure, but what meets the terms of some security
standard.Correct, IIUC in order to be FIPS compliant all cryptographic modules used must
be FIPS certified.
/me whitles, thinking about not using src/common/md5.c when building
with OpenSSL to actually get a complain if FIPS gets used.
At least in the US, FIPS 140-2 compliance is a reasonably
common need, so if we can make it easier for people who have that need
to be compliant, they are more likely to use PostgreSQL, which seems
like something that we should want.The proposed patch makes SCRAM+FIPS work for 14, question is if we need/want to
try and address v10-13.
Unfortunately, I don't have a good answer for that, except for the
answers involving an ABI breakage. FWIW, the only users of those APIs
I can find in the open wild are pgpool, which actually bundles a copy
of the code in src/common/ so it does not matter, and pgbouncer, that
uses a different compatibility layer to make the code compilable:
https://sources.debian.org/src/pgbouncer/1.14.0-1/include/common/postgres_compat.h/?hl=26#L26
But it is not really possible to say if there is any closed code
relying on that, so I'd like to fix that just on HEAD, about which I
guess there would be no objections?
--
Michael
On Fri, Sep 25, 2020 at 12:19:44PM +0900, Michael Paquier wrote:
Even if we'd try to force our internal implementation of SHA256 on
already-released branches instead of the one of OpenSSL, this would be
an ABI break for compiled modules expected to work on this released
branch as OpenSSL's internal SHA structures don't exactly match with
our own implementation (think just about sizeof() or such).
Well, we could as well add one extra SHA API layer pointing to the EVP
structures and APIs with new names, leaving the original ones in
place, and then have SCRAM use the new ones, but I'd rather not go
down that road for the back-branches.
--
Michael
Michael Paquier <michael@paquier.xyz> writes:
On Fri, Sep 25, 2020 at 12:19:44PM +0900, Michael Paquier wrote:
Even if we'd try to force our internal implementation of SHA256 on
already-released branches instead of the one of OpenSSL, this would be
an ABI break for compiled modules expected to work on this released
branch as OpenSSL's internal SHA structures don't exactly match with
our own implementation (think just about sizeof() or such).
Well, we could as well add one extra SHA API layer pointing to the EVP
structures and APIs with new names, leaving the original ones in
place, and then have SCRAM use the new ones, but I'd rather not go
down that road for the back-branches.
Given the tiny number of complaints to date, it seems sufficient to me
to deal with this in HEAD.
regards, tom lane
On 2020-09-24 21:44, Daniel Gustafsson wrote:
On 24 Sep 2020, at 21:22, Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Sep 24, 2020 at 1:57 PM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:Depends on what one considers to be covered by FIPS. The entire rest of
SCRAM is custom code, so running it on top of the world's greatest
SHA-256 implementation isn't going to make the end product any more
trustworthy.I mean, the issue here, as is so often the case, is not what is
actually more secure, but what meets the terms of some security
standard.Correct, IIUC in order to be FIPS compliant all cryptographic modules used must
be FIPS certified.
As I read FIPS 140-2, it just specifies what must be true of
cryptographic modules that claim to follow that standard, it doesn't say
that all cryptographic activity in an application or platform must only
use such modules. (Notably, it doesn't even seem to define
"cryptographic".) The latter may well be a requirement of a user or
customer on top of the actual standard. However, again, the SCRAM
implementation would already appear to fail that requirement because it
uses a custom HMAC implementation, and HMAC is listed in FIPS 140-2 as a
covered algorithm.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
On 2020-09-24 21:44, Daniel Gustafsson wrote:
Correct, IIUC in order to be FIPS compliant all cryptographic modules used must
be FIPS certified.
As I read FIPS 140-2, it just specifies what must be true of
cryptographic modules that claim to follow that standard, it doesn't say
that all cryptographic activity in an application or platform must only
use such modules. (Notably, it doesn't even seem to define
"cryptographic".) The latter may well be a requirement of a user or
customer on top of the actual standard.
Hm ... AFAICT, the intent of the "FIPS mode" in Red Hat's implementation,
and probably other Linux distros, is exactly that thou shalt not use
any non-FIPS-approved crypto code. By your reading above, there would
be no need at all for a kernel-level enforcement switch.
However, again, the SCRAM
implementation would already appear to fail that requirement because it
uses a custom HMAC implementation, and HMAC is listed in FIPS 140-2 as a
covered algorithm.
Ugh. But is there any available FIPS-approved library code that could be
used instead?
regards, tom lane
On Fri, Sep 25, 2020 at 01:36:44AM -0400, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
However, again, the SCRAM
implementation would already appear to fail that requirement because it
uses a custom HMAC implementation, and HMAC is listed in FIPS 140-2 as a
covered algorithm.Ugh. But is there any available FIPS-approved library code that could be
used instead?
That's a good point, and I think that this falls down to use OpenSSL's
HMAC_* interface for this job when building with OpenSSL:
https://www.openssl.org/docs/man1.1.1/man3/HMAC.html
Worth noting that these have been deprecated in 3.0.0 as per the
rather-recent commit dbde472, where they recommend the use of
EVP_MAC_*() instead.
--
Michael
On Fri, Sep 25, 2020 at 03:56:53PM +0900, Michael Paquier wrote:
On Fri, Sep 25, 2020 at 01:36:44AM -0400, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
However, again, the SCRAM
implementation would already appear to fail that requirement because it
uses a custom HMAC implementation, and HMAC is listed in FIPS 140-2 as a
covered algorithm.Ugh. But is there any available FIPS-approved library code that could be
used instead?That's a good point, and I think that this falls down to use OpenSSL's
HMAC_* interface for this job when building with OpenSSL:
https://www.openssl.org/docs/man1.1.1/man3/HMAC.htmlWorth noting that these have been deprecated in 3.0.0 as per the
rather-recent commit dbde472, where they recommend the use of
EVP_MAC_*() instead.
Would a FIPS server only be able to talk to a FIPS client, or would our
internal code produce the same output?
--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EnterpriseDB https://enterprisedb.com
The usefulness of a cup is in its emptiness, Bruce Lee
Bruce,
In my experience, any client is permitted to connect to FIPS140-2 compliant server. I set this up when I worked at SSA, at management’s request.
—
Jay
Sent from my iPad
Show quoted text
On Sep 25, 2020, at 3:13 PM, Bruce Momjian <bruce@momjian.us> wrote:
On Fri, Sep 25, 2020 at 03:56:53PM +0900, Michael Paquier wrote:
On Fri, Sep 25, 2020 at 01:36:44AM -0400, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:However, again, the SCRAM
implementation would already appear to fail that requirement because it
uses a custom HMAC implementation, and HMAC is listed in FIPS 140-2 as a
covered algorithm.Ugh. But is there any available FIPS-approved library code that could be
used instead?That's a good point, and I think that this falls down to use OpenSSL's
HMAC_* interface for this job when building with OpenSSL:
https://www.openssl.org/docs/man1.1.1/man3/HMAC.htmlWorth noting that these have been deprecated in 3.0.0 as per the
rather-recent commit dbde472, where they recommend the use of
EVP_MAC_*() instead.Would a FIPS server only be able to talk to a FIPS client, or would our
internal code produce the same output?--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EnterpriseDB https://enterprisedb.comThe usefulness of a cup is in its emptiness, Bruce Lee
On Fri, Sep 25, 2020 at 03:38:22PM -0400, John Scalia wrote:
Bruce,
In my experience, any client is permitted to connect to FIPS140-2 compliant server. I set this up when I worked at SSA, at management’s request.
My question is whether the hash output would match if using different
code.
--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EnterpriseDB https://enterprisedb.com
The usefulness of a cup is in its emptiness, Bruce Lee
FIPS only specifies which algorithms are approved for use on it. For instance, MD-5 is NOT approved at all under FIPS. I would say any algorithm should produce the same result regardless of where it is run. BTW, on Redhat servers, the first algorithm listed for use with SSH is MD-5. This causes the sshd daemon to abort when FIPS is enabled and that config file has not been edited. So, you can no longer connect with an SSH client as the daemon isn’t running. Ask me how I know this.
Sent from my iPad
Show quoted text
On Sep 25, 2020, at 3:39 PM, Bruce Momjian <bruce@momjian.us> wrote:
On Fri, Sep 25, 2020 at 03:38:22PM -0400, John Scalia wrote:
Bruce,
In my experience, any client is permitted to connect to FIPS140-2 compliant server. I set this up when I worked at SSA, at management’s request.
My question is whether the hash output would match if using different
code.--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EnterpriseDB https://enterprisedb.comThe usefulness of a cup is in its emptiness, Bruce Lee
On Fri, Sep 25, 2020 at 12:27:03AM -0400, Tom Lane wrote:
Given the tiny number of complaints to date, it seems sufficient to me
to deal with this in HEAD.
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().
--
Michael
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
Attachments:
0001-Rework-SHA2-APIs.patchtext/x-diff; charset=us-asciiDownload
From 44b3da02436b88a6e6cbb5671cafe2b0275c1eb5 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@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
0002-Switch-sha2_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From 3ca6db643e3eab73e948d2ef932d1811f612695f Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@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
On 14/10/2020 06:29, Michael Paquier wrote:
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.
Since this is going to be core backend code (and also frontend), we
don't need to use the generic reource owner callback mechanism, we could
add a built-in ResourceOwnerData field and functions in resowner.c. The
callback mechanism is a bit clunky.
- Heikki
On Wed, Oct 14, 2020 at 10:40:12AM +0300, Heikki Linnakangas wrote:
Since this is going to be core backend code (and also frontend), we don't
need to use the generic reource owner callback mechanism, we could add a
built-in ResourceOwnerData field and functions in resowner.c. The callback
mechanism is a bit clunky.
Sure, thanks. I wanted to keep things isolated in sha2_openssl.c as
that's something specific to the implementation. Thinking more about
it, your suggestion makes a lot of sense in the long-term by including
MD5 and HMAC in the picture. These also go through EVP in OpenSSL,
and we are kind of incorrect currently to not use the OpenSSL flavor
if available (MD5 is not authorized in FIPS, but we still allow it to
be used with the in-core implementation).
--
Michael
On Wed, Oct 14, 2020 at 05:18:51PM +0900, Michael Paquier wrote:
Sure, thanks. I wanted to keep things isolated in sha2_openssl.c as
that's something specific to the implementation. Thinking more about
it, your suggestion makes a lot of sense in the long-term by including
MD5 and HMAC in the picture. These also go through EVP in OpenSSL,
and we are kind of incorrect currently to not use the OpenSSL flavor
if available (MD5 is not authorized in FIPS, but we still allow it to
be used with the in-core implementation).
I got my hands on that, and this proves to simplify a lot things. In
bonus, attached is a 0003 that cleans up some code in pgcrypto so as
it uses the in-core resowner facility to handle EVP contexts.
--
Michael
Attachments:
v2-0001-Rework-SHA2-APIs.patchtext/x-diff; charset=us-asciiDownload
From e9c82de0b57bf6f3670da37bbf4996553e3ab9ee Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 15 Oct 2020 15:28:20 +0900
Subject: [PATCH v2 1/3] 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 | 3 +-
src/backend/libpq/auth-scram.c | 94 ++++++----
src/backend/replication/backup_manifest.c | 30 ++-
src/backend/replication/basebackup.c | 26 ++-
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 | 15 +-
src/bin/pg_verifybackup/pg_verifybackup.c | 24 ++-
src/interfaces/libpq/fe-auth-scram.c | 114 +++++++-----
contrib/pgcrypto/internal-sha2.c | 187 ++++---------------
src/tools/pgindent/typedefs.list | 1 +
17 files changed, 835 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..e427a3e9fa 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;
@@ -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
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..ccde3238da 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -62,7 +62,14 @@ InitializeBackupManifest(backup_manifest_info *manifest,
else
manifest->buffile = BufFileCreateTemp(false);
manifest->checksum_type = manifest_checksum_type;
- pg_sha256_init(&manifest->manifest_ctx);
+ if (want_manifest == MANIFEST_OPTION_NO)
+ manifest->manifest_ctx = NULL;
+ else
+ {
+ 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;
@@ -74,6 +81,16 @@ InitializeBackupManifest(backup_manifest_info *manifest,
"\"Files\": [");
}
+/*
+ * Free resources assigned to a backup manifest constructed.
+ */
+void
+FreeBackupManifest(backup_manifest_info *manifest)
+{
+ pg_sha2_free(manifest->manifest_ctx);
+ manifest->manifest_ctx = NULL;
+}
+
/*
* Add an entry to the backup manifest for a file.
*/
@@ -161,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\": \"",
@@ -305,7 +325,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';
@@ -368,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_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..ddd2138347 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,
@@ -730,6 +730,7 @@ perform_base_backup(basebackup_options *opt)
}
/* clean up the resource owner we created */
+ FreeBackupManifest(&manifest);
WalSndResourceCleanup(true);
pgstat_progress_end_command();
@@ -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);
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..e3e4f54ea9 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)
@@ -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_sha2_free(manifest_ctx);
}
/*
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);
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index c52f20d4ba..149691c26c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3180,6 +3180,7 @@ pg_mb_radix_tree
pg_on_exit_callback
pg_re_flags
pg_saslprep_rc
+pg_sha2_ctx
pg_sha224_ctx
pg_sha256_ctx
pg_sha384_ctx
--
2.28.0
v2-0002-Switch-sha2_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From 117f9c2d277d8bb778bd1307d740cd7c6b52b9e9 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 15 Oct 2020 10:40:06 +0900
Subject: [PATCH v2 2/3] Switch sha2_openssl.c to use EVP
Postgres is two decades late for this switch.
---
src/include/utils/resowner_private.h | 7 +++
src/backend/utils/resowner/resowner.c | 65 ++++++++++++++++++++
src/common/sha2_openssl.c | 88 +++++++++++++--------------
3 files changed, 113 insertions(+), 47 deletions(-)
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..5ce6fcf882 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
Datum handle);
+/* support for EVP context management */
+extern void ResourceOwnerEnlargeEVP(ResourceOwner owner);
+extern void ResourceOwnerRememberEVP(ResourceOwner owner,
+ Datum handle);
+extern void ResourceOwnerForgetEVP(ResourceOwner owner,
+ Datum handle);
+
#endif /* RESOWNER_PRIVATE_H */
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..1efb5e98b4 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,10 @@
*/
#include "postgres.h"
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
@@ -128,6 +132,7 @@ typedef struct ResourceOwnerData
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray evparr; /* EVP contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
@@ -175,6 +180,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintEVPLeakWarning(Datum handle);
/*****************************************************************************
@@ -444,6 +450,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->evparr), PointerGetDatum(NULL));
return owner;
}
@@ -553,6 +560,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
jit_release_context(context);
}
+
+ /* Ditto for EVP contexts */
+ while (ResourceArrayGetAny(&(owner->evparr), &foundres))
+ {
+ if (isCommit)
+ PrintEVPLeakWarning(foundres);
+#ifdef USE_OPENSSL
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) DatumGetPointer(foundres));
+#endif
+ ResourceOwnerForgetEVP(owner, foundres);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
@@ -725,6 +743,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->evparr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
@@ -752,6 +771,7 @@ ResourceOwnerDelete(ResourceOwner owner)
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->evparr));
pfree(owner);
}
@@ -1370,3 +1390,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * EVP context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeEVP(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->evparr));
+}
+
+/*
+ * Remember that an EVP context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeEVP()
+ */
+void
+ResourceOwnerRememberEVP(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->evparr), handle);
+}
+
+/*
+ * Forget that an EVP context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetEVP(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->evparr), handle))
+ elog(ERROR, "EVP context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintEVPLeakWarning(Datum handle)
+{
+ elog(WARNING, "EVP context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
index 57de96df90..8cfe69cd69 100644
--- a/src/common/sha2_openssl.c
+++ b/src/common/sha2_openssl.c
@@ -20,9 +20,14 @@
#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"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
@@ -52,25 +57,31 @@ pg_sha2_create(pg_sha2_type type)
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;
- }
+#ifndef FRONTEND
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ ctx->data = EVP_MD_CTX_create();
if (ctx->data == NULL)
{
explicit_bzero(ctx, sizeof(pg_sha2_ctx));
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
+#else
FREE(ctx);
return NULL;
+#endif
}
+#ifndef FRONTEND
+ ResourceOwnerRememberEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
return ctx;
}
@@ -90,16 +101,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;
}
@@ -122,21 +137,7 @@ pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len)
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)
@@ -157,21 +158,7 @@ pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest)
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)
@@ -189,7 +176,14 @@ pg_sha2_free(pg_sha2_ctx *ctx)
{
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
explicit_bzero(ctx, sizeof(pg_sha2_ctx));
FREE(ctx);
}
--
2.28.0
v2-0003-Move-pgcrypto-to-use-in-core-resowner-facility-fo.patchtext/x-diff; charset=us-asciiDownload
From f3ef89d83efd7aeaa9e0186c8cf9c497db5ab4e1 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 15 Oct 2020 15:49:01 +0900
Subject: [PATCH v2 3/3] Move pgcrypto to use in-core resowner facility for EVP
This simplifies some code in pgcrypto as it does not need anymore to
rely on its own internal logic for the EVP registration and automatic
cleanup.
---
contrib/pgcrypto/openssl.c | 88 +++++---------------------------------
1 file changed, 11 insertions(+), 77 deletions(-)
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 90951a8ae7..44d612284e 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -38,6 +38,7 @@
#include "px.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
+#include "utils/resowner_private.h"
/*
* Max lengths we might want to handle.
@@ -49,67 +50,12 @@
* Hashes
*/
-/*
- * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
- * objects in a linked list, allocated in TopMemoryContext. We use the
- * ResourceOwner mechanism to free them on abort.
- */
typedef struct OSSLDigest
{
const EVP_MD *algo;
EVP_MD_CTX *ctx;
-
- ResourceOwner owner;
- struct OSSLDigest *next;
- struct OSSLDigest *prev;
} OSSLDigest;
-static OSSLDigest *open_digests = NULL;
-static bool digest_resowner_callback_registered = false;
-
-static void
-free_openssl_digest(OSSLDigest *digest)
-{
- EVP_MD_CTX_destroy(digest->ctx);
- if (digest->prev)
- digest->prev->next = digest->next;
- else
- open_digests = digest->next;
- if (digest->next)
- digest->next->prev = digest->prev;
- pfree(digest);
-}
-
-/*
- * Close any open OpenSSL handles on abort.
- */
-static void
-digest_free_callback(ResourceReleasePhase phase,
- bool isCommit,
- bool isTopLevel,
- void *arg)
-{
- OSSLDigest *curr;
- OSSLDigest *next;
-
- if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
- return;
-
- next = open_digests;
- while (next)
- {
- curr = next;
- next = curr->next;
-
- if (curr->owner == CurrentResourceOwner)
- {
- if (isCommit)
- elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
- free_openssl_digest(curr);
- }
- }
-}
-
static unsigned
digest_result_size(PX_MD *h)
{
@@ -155,7 +101,10 @@ digest_free(PX_MD *h)
{
OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
- free_openssl_digest(digest);
+ EVP_MD_CTX_destroy(digest->ctx);
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(digest->ctx));
+ pfree(digest);
pfree(h);
}
@@ -177,41 +126,26 @@ px_find_digest(const char *name, PX_MD **res)
OpenSSL_add_all_algorithms();
}
- if (!digest_resowner_callback_registered)
- {
- RegisterResourceReleaseCallback(digest_free_callback, NULL);
- digest_resowner_callback_registered = true;
- }
-
md = EVP_get_digestbyname(name);
if (md == NULL)
return PXE_NO_HASH;
- /*
- * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
- * The order is crucial, to make sure we don't leak anything on
- * out-of-memory or other error.
- */
- digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest));
-
ctx = EVP_MD_CTX_create();
if (!ctx)
- {
- pfree(digest);
return -1;
- }
+
if (EVP_DigestInit_ex(ctx, md, NULL) == 0)
{
- pfree(digest);
+ EVP_MD_CTX_destroy(ctx);
return -1;
}
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+ ResourceOwnerRememberEVP(CurrentResourceOwner, PointerGetDatum(ctx));
+
+ digest = palloc(sizeof(*digest));
digest->algo = md;
digest->ctx = ctx;
- digest->owner = CurrentResourceOwner;
- digest->next = open_digests;
- digest->prev = NULL;
- open_digests = digest;
/* The PX_MD object is allocated in the current memory context. */
h = palloc(sizeof(*h));
--
2.28.0
On Thu, Oct 15, 2020 at 03:56:21PM +0900, Michael Paquier wrote:
I got my hands on that, and this proves to simplify a lot things. In
bonus, attached is a 0003 that cleans up some code in pgcrypto so as
it uses the in-core resowner facility to handle EVP contexts.
This conflicted on HEAD with pgcrypto. Please find attached a rebased
set.
--
Michael
Attachments:
v3-0001-Rework-SHA2-APIs.patchtext/x-diff; charset=us-asciiDownload
From afa4c22040791d3c4a57df5fa3b913ca127d5233 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 5 Nov 2020 15:29:54 +0900
Subject: [PATCH v3 1/3] 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 | 3 +-
src/backend/libpq/auth-scram.c | 94 ++++++----
src/backend/replication/backup_manifest.c | 30 ++-
src/backend/replication/basebackup.c | 26 ++-
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 | 15 +-
src/bin/pg_verifybackup/pg_verifybackup.c | 24 ++-
src/interfaces/libpq/fe-auth-scram.c | 114 +++++++-----
contrib/pgcrypto/internal-sha2.c | 187 ++++---------------
src/tools/pgindent/typedefs.list | 1 +
17 files changed, 835 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..e427a3e9fa 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;
@@ -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
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 556e6b5040..1a3b380959 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -62,7 +62,14 @@ InitializeBackupManifest(backup_manifest_info *manifest,
else
manifest->buffile = BufFileCreateTemp(false);
manifest->checksum_type = manifest_checksum_type;
- pg_sha256_init(&manifest->manifest_ctx);
+ if (want_manifest == MANIFEST_OPTION_NO)
+ manifest->manifest_ctx = NULL;
+ else
+ {
+ 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;
@@ -74,6 +81,16 @@ InitializeBackupManifest(backup_manifest_info *manifest,
"\"Files\": [");
}
+/*
+ * Free resources assigned to a backup manifest constructed.
+ */
+void
+FreeBackupManifest(backup_manifest_info *manifest)
+{
+ pg_sha2_free(manifest->manifest_ctx);
+ manifest->manifest_ctx = NULL;
+}
+
/*
* Add an entry to the backup manifest for a file.
*/
@@ -161,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\": \"",
@@ -305,7 +325,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';
@@ -368,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_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..ddd2138347 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,
@@ -730,6 +730,7 @@ perform_base_backup(basebackup_options *opt)
}
/* clean up the resource owner we created */
+ FreeBackupManifest(&manifest);
WalSndResourceCleanup(true);
pgstat_progress_end_command();
@@ -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);
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..e3e4f54ea9 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)
@@ -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_sha2_free(manifest_ctx);
}
/*
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);
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index f2ba92be53..7f7e6b7af5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3190,6 +3190,7 @@ pg_mb_radix_tree
pg_on_exit_callback
pg_re_flags
pg_saslprep_rc
+pg_sha2_ctx
pg_sha224_ctx
pg_sha256_ctx
pg_sha384_ctx
--
2.29.1
v3-0002-Switch-sha2_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From 12b8786a4525306e78e252340b026531f6d04933 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 5 Nov 2020 15:29:54 +0900
Subject: [PATCH v3 2/3] Switch sha2_openssl.c to use EVP
Postgres is two decades late for this switch.
---
src/include/utils/resowner_private.h | 7 +++
src/backend/utils/resowner/resowner.c | 65 ++++++++++++++++++++
src/common/sha2_openssl.c | 88 +++++++++++++--------------
3 files changed, 113 insertions(+), 47 deletions(-)
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..5ce6fcf882 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
Datum handle);
+/* support for EVP context management */
+extern void ResourceOwnerEnlargeEVP(ResourceOwner owner);
+extern void ResourceOwnerRememberEVP(ResourceOwner owner,
+ Datum handle);
+extern void ResourceOwnerForgetEVP(ResourceOwner owner,
+ Datum handle);
+
#endif /* RESOWNER_PRIVATE_H */
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..1efb5e98b4 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,10 @@
*/
#include "postgres.h"
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
@@ -128,6 +132,7 @@ typedef struct ResourceOwnerData
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray evparr; /* EVP contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
@@ -175,6 +180,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintEVPLeakWarning(Datum handle);
/*****************************************************************************
@@ -444,6 +450,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->evparr), PointerGetDatum(NULL));
return owner;
}
@@ -553,6 +560,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
jit_release_context(context);
}
+
+ /* Ditto for EVP contexts */
+ while (ResourceArrayGetAny(&(owner->evparr), &foundres))
+ {
+ if (isCommit)
+ PrintEVPLeakWarning(foundres);
+#ifdef USE_OPENSSL
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) DatumGetPointer(foundres));
+#endif
+ ResourceOwnerForgetEVP(owner, foundres);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
@@ -725,6 +743,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->evparr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
@@ -752,6 +771,7 @@ ResourceOwnerDelete(ResourceOwner owner)
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->evparr));
pfree(owner);
}
@@ -1370,3 +1390,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * EVP context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeEVP(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->evparr));
+}
+
+/*
+ * Remember that an EVP context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeEVP()
+ */
+void
+ResourceOwnerRememberEVP(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->evparr), handle);
+}
+
+/*
+ * Forget that an EVP context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetEVP(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->evparr), handle))
+ elog(ERROR, "EVP context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintEVPLeakWarning(Datum handle)
+{
+ elog(WARNING, "EVP context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
index 57de96df90..8cfe69cd69 100644
--- a/src/common/sha2_openssl.c
+++ b/src/common/sha2_openssl.c
@@ -20,9 +20,14 @@
#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"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
@@ -52,25 +57,31 @@ pg_sha2_create(pg_sha2_type type)
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;
- }
+#ifndef FRONTEND
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ ctx->data = EVP_MD_CTX_create();
if (ctx->data == NULL)
{
explicit_bzero(ctx, sizeof(pg_sha2_ctx));
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
+#else
FREE(ctx);
return NULL;
+#endif
}
+#ifndef FRONTEND
+ ResourceOwnerRememberEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
return ctx;
}
@@ -90,16 +101,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;
}
@@ -122,21 +137,7 @@ pg_sha2_update(pg_sha2_ctx *ctx, const uint8 *data, size_t len)
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)
@@ -157,21 +158,7 @@ pg_sha2_final(pg_sha2_ctx *ctx, uint8 *dest)
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)
@@ -189,7 +176,14 @@ pg_sha2_free(pg_sha2_ctx *ctx)
{
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
explicit_bzero(ctx, sizeof(pg_sha2_ctx));
FREE(ctx);
}
--
2.29.1
v3-0003-Move-pgcrypto-to-use-in-core-resowner-facility-fo.patchtext/x-diff; charset=us-asciiDownload
From 5138e31e3dd1a85e4c91811cddbad1650efbf147 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 5 Nov 2020 15:34:55 +0900
Subject: [PATCH v3 3/3] Move pgcrypto to use in-core resowner facility for EVP
This simplifies some code in pgcrypto as it does not need anymore to
rely on its own internal logic for the EVP registration and automatic
cleanup.
---
contrib/pgcrypto/openssl.c | 87 +++++---------------------------------
1 file changed, 10 insertions(+), 77 deletions(-)
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 5ebe213406..d8e1269a40 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -38,6 +38,7 @@
#include "px.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
+#include "utils/resowner_private.h"
/*
* Max lengths we might want to handle.
@@ -49,67 +50,12 @@
* Hashes
*/
-/*
- * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
- * objects in a linked list, allocated in TopMemoryContext. We use the
- * ResourceOwner mechanism to free them on abort.
- */
typedef struct OSSLDigest
{
const EVP_MD *algo;
EVP_MD_CTX *ctx;
-
- ResourceOwner owner;
- struct OSSLDigest *next;
- struct OSSLDigest *prev;
} OSSLDigest;
-static OSSLDigest *open_digests = NULL;
-static bool digest_resowner_callback_registered = false;
-
-static void
-free_openssl_digest(OSSLDigest *digest)
-{
- EVP_MD_CTX_destroy(digest->ctx);
- if (digest->prev)
- digest->prev->next = digest->next;
- else
- open_digests = digest->next;
- if (digest->next)
- digest->next->prev = digest->prev;
- pfree(digest);
-}
-
-/*
- * Close any open OpenSSL handles on abort.
- */
-static void
-digest_free_callback(ResourceReleasePhase phase,
- bool isCommit,
- bool isTopLevel,
- void *arg)
-{
- OSSLDigest *curr;
- OSSLDigest *next;
-
- if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
- return;
-
- next = open_digests;
- while (next)
- {
- curr = next;
- next = curr->next;
-
- if (curr->owner == CurrentResourceOwner)
- {
- if (isCommit)
- elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
- free_openssl_digest(curr);
- }
- }
-}
-
static unsigned
digest_result_size(PX_MD *h)
{
@@ -155,7 +101,10 @@ digest_free(PX_MD *h)
{
OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
- free_openssl_digest(digest);
+ EVP_MD_CTX_destroy(digest->ctx);
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(digest->ctx));
+ pfree(digest);
pfree(h);
}
@@ -177,42 +126,26 @@ px_find_digest(const char *name, PX_MD **res)
OpenSSL_add_all_algorithms();
}
- if (!digest_resowner_callback_registered)
- {
- RegisterResourceReleaseCallback(digest_free_callback, NULL);
- digest_resowner_callback_registered = true;
- }
-
md = EVP_get_digestbyname(name);
if (md == NULL)
return PXE_NO_HASH;
- /*
- * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
- * The order is crucial, to make sure we don't leak anything on
- * out-of-memory or other error.
- */
- digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest));
-
ctx = EVP_MD_CTX_create();
if (!ctx)
- {
- pfree(digest);
return -1;
- }
+
if (EVP_DigestInit_ex(ctx, md, NULL) == 0)
{
EVP_MD_CTX_destroy(ctx);
- pfree(digest);
return -1;
}
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+ ResourceOwnerRememberEVP(CurrentResourceOwner, PointerGetDatum(ctx));
+
+ digest = palloc(sizeof(*digest));
digest->algo = md;
digest->ctx = ctx;
- digest->owner = CurrentResourceOwner;
- digest->next = open_digests;
- digest->prev = NULL;
- open_digests = digest;
/* The PX_MD object is allocated in the current memory context. */
h = palloc(sizeof(*h));
--
2.29.1
On Thu, Nov 05, 2020 at 03:41:23PM +0900, Michael Paquier wrote:
This conflicted on HEAD with pgcrypto. Please find attached a rebased
set.
I got to think more about this stuff and attached is a new patch set
that redesigns the generic interface used for the crypto hash
functions, in order to use the same entry point at the end for SHA2,
SHA1, MD5 or even HMAC. This is part of 0001:
- Introduction of a single file called cryptohash[_openssl].c, which
includes five functions to create, initialize, update, finalize and
free a crypto hash context. The attached does the work for SHA2.
- The fallback implementations are in their own file in src/common/,
and get included in cryptohash.c. cryptohash_openssl.c is much more
simple as it needs to use EVP for everything.
- Adding a new crypto function in the set is simple once this is done,
as a type needs to be added with the correct options plugged in.
0002 and 0003 don't have any changes. I think that we could also
rename the existing cryptohashes.c to crypohashfuncs.c to be more
consistent, but I have left that out for now.
--
Michael
Attachments:
v4-0001-Rework-SHA2-and-crypto-hash-APIs.patchtext/x-diff; charset=us-asciiDownload
From 7175bb4610114c4d04c43c35e176d4c67241ebdd Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 13 Nov 2020 11:47:25 +0900
Subject: [PATCH v4 1/3] Rework SHA2 and crypto hash APIs
This will make easier a switch to EVP for the OpenSSL SHA2 layer. Note
that the layer introduced here is generalized for the purpose of a
future integration with HMAC, MD5, and even more.
---
src/include/common/checksum_helper.h | 13 +-
src/include/common/cryptohash.h | 40 +++++
src/include/common/scram-common.h | 17 +-
src/include/common/sha2.h | 51 +-----
src/include/replication/backup_manifest.h | 3 +-
src/backend/libpq/auth-scram.c | 94 +++++++----
src/backend/replication/backup_manifest.c | 25 ++-
src/backend/replication/basebackup.c | 24 ++-
src/backend/utils/adt/cryptohashes.c | 53 ++++--
src/common/Makefile | 7 +-
src/common/checksum_helper.c | 79 +++++++--
src/common/cryptohash.c | 191 +++++++++++++++++++++
src/common/cryptohash_openssl.c | 196 ++++++++++++++++++++++
src/common/scram-common.c | 165 ++++++++++++------
src/common/sha2.c | 63 +++++--
src/common/sha2_openssl.c | 102 -----------
src/bin/pg_verifybackup/parse_manifest.c | 15 +-
src/bin/pg_verifybackup/pg_verifybackup.c | 24 ++-
src/interfaces/libpq/fe-auth-scram.c | 114 +++++++------
contrib/pgcrypto/internal-sha2.c | 188 +++++----------------
src/tools/msvc/Mkvcbuild.pm | 4 +-
src/tools/pgindent/typedefs.list | 1 +
22 files changed, 951 insertions(+), 518 deletions(-)
create mode 100644 src/include/common/cryptohash.h
create mode 100644 src/common/cryptohash.c
create mode 100644 src/common/cryptohash_openssl.c
delete mode 100644 src/common/sha2_openssl.c
diff --git a/src/include/common/checksum_helper.h b/src/include/common/checksum_helper.h
index 48b0745dad..b07a34e7e4 100644
--- a/src/include/common/checksum_helper.h
+++ b/src/include/common/checksum_helper.h
@@ -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
index 0000000000..775c975451
--- /dev/null
+++ b/src/include/common/cryptohash.h
@@ -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_ */
diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h
index 2edae2dd3c..f4a7c60725 100644
--- a/src/include/common/scram-common.h
+++ b/src/include/common/scram-common.h
@@ -13,6 +13,7 @@
#ifndef SCRAM_COMMON_H
#define SCRAM_COMMON_H
+#include "common/cryptohash.h"
#include "common/sha2.h"
/* Name of SCRAM mechanisms per IANA */
@@ -50,19 +51,19 @@
*/
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);
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index 9c4abf777d..e36e22ac76 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -3,7 +3,8 @@
* sha2.h
* Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
*
- * 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
@@ -50,10 +51,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
@@ -68,48 +65,4 @@
#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_ */
diff --git a/src/include/replication/backup_manifest.h b/src/include/replication/backup_manifest.h
index fb1291cbe4..e7c4047497 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_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
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 0f79b28bb5..5a54bdd353 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_cryptohash_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_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;
}
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index bab5e2f53b..c3f339c556 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -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;
}
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index b89df01fa7..0969036c7d 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -730,6 +730,7 @@ perform_base_backup(basebackup_options *opt)
}
/* clean up the resource owner we created */
+ FreeBackupManifest(&manifest);
WalSndResourceCleanup(true);
pgstat_progress_end_command();
@@ -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);
diff --git a/src/backend/utils/adt/cryptohashes.c b/src/backend/utils/adt/cryptohashes.c
index e897660927..5de294a7fd 100644
--- a/src/backend/utils/adt/cryptohashes.c
+++ b/src/backend/utils/adt/cryptohashes.c
@@ -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);
diff --git a/src/common/Makefile b/src/common/Makefile
index 25c55bd642..e5492933d4 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -82,9 +82,9 @@ 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
endif
# A few files are currently only built for frontend, not server
@@ -172,6 +172,9 @@ kwlist_d.h: $(top_srcdir)/src/include/parser/kwlist.h $(GEN_KEYWORDLIST_DEPS)
# that you don't get broken parsing code, even in a non-enable-depend build.
keywords.o keywords_shlib.o keywords_srv.o: kwlist_d.h
+# Fallback cryptographic implementations need the those files.
+cryptohash.o: cryptohash.c sha2.c
+
# The code imported from Ryu gets a pass on declaration-after-statement,
# in order to keep it more closely aligned with its upstream.
RYU_FILES = d2s.o f2s.o
diff --git a/src/common/checksum_helper.c b/src/common/checksum_helper.c
index 79a9a7447b..8e06524cd3 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_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
index 0000000000..e693a1e290
--- /dev/null
+++ b/src/common/cryptohash.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 internals of each implementation here */
+#include "sha2.c"
+
+/*
+ * 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.
+ */
+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
index 0000000000..406abf6163
--- /dev/null
+++ b/src/common/cryptohash_openssl.c
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.
+ */
+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);
+}
diff --git a/src/common/scram-common.c b/src/common/scram-common.c
index 4971134b22..04082414e5 100644
--- a/src/common/scram-common.c
+++ b/src/common/scram-common.c
@@ -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_cryptohash_ctx *sha256_ctx;
- pg_sha256_init(&sha256_ctx);
- pg_sha256_update(&sha256_ctx, key, keylen);
- pg_sha256_final(&sha256_ctx, keybuf);
+ 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,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_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 +268,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..15911a3414 100644
--- a/src/common/sha2.c
+++ b/src/common/sha2.c
@@ -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
@@ -56,10 +57,36 @@
#include "postgres_fe.h"
#endif
-#include <sys/param.h>
-
#include "common/sha2.h"
+/* Internal structures used for the private area of pg_cryptohash_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 +291,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 +488,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 +589,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 +617,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 +814,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 +917,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 +946,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 +956,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 +990,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 +1000,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: */
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
deleted file mode 100644
index 41673b3a88..0000000000
--- a/src/common/sha2_openssl.c
+++ /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);
-}
diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c
index 608e23538b..5b4ce28837 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_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);
}
/*
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..0fe53e15af 100644
--- a/contrib/pgcrypto/internal-sha2.c
+++ b/contrib/pgcrypto/internal-sha2.c
@@ -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);
}
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 90594bd41b..af15d3db6c 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -129,12 +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, 'sha2.c');
+ push(@pgcommonallfiles, 'cryptohash.c');
}
our @pgcommonfrontendfiles = (
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 0e4bb4f07f..0217f76add 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3191,6 +3191,7 @@ pg_mb_radix_tree
pg_on_exit_callback
pg_re_flags
pg_saslprep_rc
+pg_cryptohash_ctx
pg_sha224_ctx
pg_sha256_ctx
pg_sha384_ctx
--
2.29.2
v4-0002-Switch-cryptohash_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From a92306674f07c82a920250486c93671648dcdc4d Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 13 Nov 2020 11:50:21 +0900
Subject: [PATCH v4 2/3] Switch cryptohash_openssl.c to use EVP
Postgres is two decades late for this switch.
---
src/include/utils/resowner_private.h | 7 +++
src/backend/utils/resowner/resowner.c | 65 ++++++++++++++++++++
src/common/cryptohash_openssl.c | 88 +++++++++++++--------------
3 files changed, 113 insertions(+), 47 deletions(-)
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..5ce6fcf882 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
Datum handle);
+/* support for EVP context management */
+extern void ResourceOwnerEnlargeEVP(ResourceOwner owner);
+extern void ResourceOwnerRememberEVP(ResourceOwner owner,
+ Datum handle);
+extern void ResourceOwnerForgetEVP(ResourceOwner owner,
+ Datum handle);
+
#endif /* RESOWNER_PRIVATE_H */
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..1efb5e98b4 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,10 @@
*/
#include "postgres.h"
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
@@ -128,6 +132,7 @@ typedef struct ResourceOwnerData
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray evparr; /* EVP contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
@@ -175,6 +180,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintEVPLeakWarning(Datum handle);
/*****************************************************************************
@@ -444,6 +450,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->evparr), PointerGetDatum(NULL));
return owner;
}
@@ -553,6 +560,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
jit_release_context(context);
}
+
+ /* Ditto for EVP contexts */
+ while (ResourceArrayGetAny(&(owner->evparr), &foundres))
+ {
+ if (isCommit)
+ PrintEVPLeakWarning(foundres);
+#ifdef USE_OPENSSL
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) DatumGetPointer(foundres));
+#endif
+ ResourceOwnerForgetEVP(owner, foundres);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
@@ -725,6 +743,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->evparr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
@@ -752,6 +771,7 @@ ResourceOwnerDelete(ResourceOwner owner)
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->evparr));
pfree(owner);
}
@@ -1370,3 +1390,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * EVP context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeEVP(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->evparr));
+}
+
+/*
+ * Remember that an EVP context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeEVP()
+ */
+void
+ResourceOwnerRememberEVP(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->evparr), handle);
+}
+
+/*
+ * Forget that an EVP context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetEVP(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->evparr), handle))
+ elog(ERROR, "EVP context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintEVPLeakWarning(Datum handle)
+{
+ elog(WARNING, "EVP context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 406abf6163..cde3c9d597 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -21,9 +21,14 @@
#include "postgres_fe.h"
#endif
-#include <openssl/sha.h>
+#include <openssl/evp.h>
#include "common/cryptohash.h"
+#ifndef FRONTEND
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
@@ -53,25 +58,31 @@ pg_cryptohash_create(pg_cryptohash_type type)
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;
- }
+#ifndef FRONTEND
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ ctx->data = EVP_MD_CTX_create();
if (ctx->data == NULL)
{
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
+#else
FREE(ctx);
return NULL;
+#endif
}
+#ifndef FRONTEND
+ ResourceOwnerRememberEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
return ctx;
}
@@ -91,16 +102,20 @@ pg_cryptohash_init(pg_cryptohash_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;
}
@@ -123,21 +138,7 @@ pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
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)
@@ -158,21 +159,7 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
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)
@@ -190,7 +177,14 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
}
--
2.29.2
v4-0003-Move-pgcrypto-to-use-in-core-resowner-facility-fo.patchtext/x-diff; charset=us-asciiDownload
From 499530b170dcd4b5ec2b519284f835171a9cee1f Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 5 Nov 2020 15:34:55 +0900
Subject: [PATCH v4 3/3] Move pgcrypto to use in-core resowner facility for EVP
This simplifies some code in pgcrypto as it does not need anymore to
rely on its own internal logic for the EVP registration and automatic
cleanup.
---
contrib/pgcrypto/openssl.c | 87 +++++---------------------------------
1 file changed, 10 insertions(+), 77 deletions(-)
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 5ebe213406..d8e1269a40 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -38,6 +38,7 @@
#include "px.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
+#include "utils/resowner_private.h"
/*
* Max lengths we might want to handle.
@@ -49,67 +50,12 @@
* Hashes
*/
-/*
- * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
- * objects in a linked list, allocated in TopMemoryContext. We use the
- * ResourceOwner mechanism to free them on abort.
- */
typedef struct OSSLDigest
{
const EVP_MD *algo;
EVP_MD_CTX *ctx;
-
- ResourceOwner owner;
- struct OSSLDigest *next;
- struct OSSLDigest *prev;
} OSSLDigest;
-static OSSLDigest *open_digests = NULL;
-static bool digest_resowner_callback_registered = false;
-
-static void
-free_openssl_digest(OSSLDigest *digest)
-{
- EVP_MD_CTX_destroy(digest->ctx);
- if (digest->prev)
- digest->prev->next = digest->next;
- else
- open_digests = digest->next;
- if (digest->next)
- digest->next->prev = digest->prev;
- pfree(digest);
-}
-
-/*
- * Close any open OpenSSL handles on abort.
- */
-static void
-digest_free_callback(ResourceReleasePhase phase,
- bool isCommit,
- bool isTopLevel,
- void *arg)
-{
- OSSLDigest *curr;
- OSSLDigest *next;
-
- if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
- return;
-
- next = open_digests;
- while (next)
- {
- curr = next;
- next = curr->next;
-
- if (curr->owner == CurrentResourceOwner)
- {
- if (isCommit)
- elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
- free_openssl_digest(curr);
- }
- }
-}
-
static unsigned
digest_result_size(PX_MD *h)
{
@@ -155,7 +101,10 @@ digest_free(PX_MD *h)
{
OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
- free_openssl_digest(digest);
+ EVP_MD_CTX_destroy(digest->ctx);
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(digest->ctx));
+ pfree(digest);
pfree(h);
}
@@ -177,42 +126,26 @@ px_find_digest(const char *name, PX_MD **res)
OpenSSL_add_all_algorithms();
}
- if (!digest_resowner_callback_registered)
- {
- RegisterResourceReleaseCallback(digest_free_callback, NULL);
- digest_resowner_callback_registered = true;
- }
-
md = EVP_get_digestbyname(name);
if (md == NULL)
return PXE_NO_HASH;
- /*
- * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
- * The order is crucial, to make sure we don't leak anything on
- * out-of-memory or other error.
- */
- digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest));
-
ctx = EVP_MD_CTX_create();
if (!ctx)
- {
- pfree(digest);
return -1;
- }
+
if (EVP_DigestInit_ex(ctx, md, NULL) == 0)
{
EVP_MD_CTX_destroy(ctx);
- pfree(digest);
return -1;
}
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+ ResourceOwnerRememberEVP(CurrentResourceOwner, PointerGetDatum(ctx));
+
+ digest = palloc(sizeof(*digest));
digest->algo = md;
digest->ctx = ctx;
- digest->owner = CurrentResourceOwner;
- digest->next = open_digests;
- digest->prev = NULL;
- open_digests = digest;
/* The PX_MD object is allocated in the current memory context. */
h = palloc(sizeof(*h));
--
2.29.2
On 13 Nov 2020, at 04:14, Michael Paquier <michael@paquier.xyz> wrote:
I got to think more about this stuff and attached is a new patch set
that redesigns the generic interface used for the crypto hash
functions
I've spent some time on this, and the gist of the patchset is clearly something
we want to do: move to the EVP API and ensure that we don't drop any contexts.
IIUC, this patchset moves the allocation of the context inside the API rather
than having the caller be responsible for providing the memory (and thus be
able to use the stack). This in turn changes the API to returning int rather
than being void.
As an effect of this we get the below types of statements that aren't easy on
the eyes:
+ /*
+ * 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");
This seems like a complication which brings little benefit since the only real
errorcase is OOM in creating the context? The built-in crypto support is
designed to never fail, and reading the OpenSSL code the only failure cases are
ENGINE initialization (which we don't do) and memory allocation. Did you
consider using EVP_MD_CTX_init instead which place the memory allocation
responsibility with the caller? Keeping this a void API and leaving the caller
to decide on memory allocation would make the API a lot simpler IMHO.
Are there other error cases and/or errorpaths you're considering that I'm
missing here? If it's just OOM on allocation it seems a high price to pay.
+#ifndef _PG_CRYPTOHASH_H_
+#define _PG_CRYPTOHASH_H_
This should probably be CRYPTOHASH_H to be consistent?
+ status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data,
+ EVP_sha224(), NULL);
Since we always use the default implementation and never select an ENGINE, why
not use EVP_DigestInit instead?
+/* Include internals of each implementation here */
+#include "sha2.c"
Do we really want to implement this by including a .c file? Are there no other
options you see?
I think that we could also rename the existing cryptohashes.c to
crypohashfuncs.c to be more consistent, but I have left that out for now.
+1. That can be done as a separate patch submission though as it's unrelated.
cheers ./daniel
On Thu, Nov 19, 2020 at 02:05:35PM +0100, Daniel Gustafsson wrote:
IIUC, this patchset moves the allocation of the context inside the API rather
than having the caller be responsible for providing the memory (and thus be
able to use the stack). This in turn changes the API to returning int rather
than being void.As an effect of this we get the below types of statements that aren't easy on
the eyes:[... code ...]
We use this style in other places of the code. Slitting each part can
also be done, if that's easier for the eye. Personal taste likely ;)
This seems like a complication which brings little benefit since the only real
errorcase is OOM in creating the context? The built-in crypto support is
designed to never fail, and reading the OpenSSL code the only failure cases are
ENGINE initialization (which we don't do) and memory allocation. Did you
consider using EVP_MD_CTX_init instead which place the memory allocation
responsibility with the caller? Keeping this a void API and leaving the caller
to decide on memory allocation would make the API a lot simpler IMHO.
Yes. That's actually the main take and why EVP callers *have* to let
OpenSSL do the allocation: we cannot know the size of EVP_MD_CTX in
advance in newer versions, and OpenSSL visibly want to keep the right
to do extra allocations if necessary within what's set in the context.
This is based on my reads of the upstream code as of
crypto/evp/digest.c and crypto/evp/evp.h. evp.h includes
env_md_ctx_st for OpenSSL <= 1.0.2, but this has been moved to
evp_local.h, header not installed, in newer versions which refers only
to the internals of the thing, with ossl_typ.h still making EVP_MD_CTX
point to env_md_ctx_st, but it becomes an incomplete type. On top of
that, knowing that we still need to deal with the OOM failure handling
and pass down the error to the callers playing with SHA2, it feels
like the most consistent API to me for the frontend and the backend.
If you want, it is easy to test that with stuff like that in
cryptohashes.c:
+ EVP_MD_CTX *ctx;
[...]
+ ctx = palloc(sizeof(EVP_MD_CTX));
+
+ EVP_MD_CTX_init(ctx);
+
+ EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
+ EVP_DigestUpdate(ctx, data, len);
+ EVP_DigestFinal_ex(ctx, buf, 0);
FWIW, this thread has dealt with the problem for pgcrypto:
/messages/by-id/20160701094159.GA17882@msg.df7cb.de
+#ifndef _PG_CRYPTOHASH_H_ +#define _PG_CRYPTOHASH_H_ This should probably be CRYPTOHASH_H to be consistent?
cryptohash.h sounds like a header file we could find somewhere else,
hence the extra PG_.
+ status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data, + EVP_sha224(), NULL); Since we always use the default implementation and never select an ENGINE, why not use EVP_DigestInit instead?
Indeed, let's do that.
+/* Include internals of each implementation here */ +#include "sha2.c" Do we really want to implement this by including a .c file? Are there no other options you see?
That was the least intrusive option I could figure out. Two other
options I have thought about:
- Compile those fallback implementations independently and publish the
internals in a separate header, but nobody is going to need that if we
have a generic entry point.
- Include the internals of each implementation in cryptohash.c, but
this bloats the file with more implementations added (HMAC and MD5
still need to be added on top of SHA2), and it messes up with the
existing copyright entries.
So splitting and just including them sounds like the cleanest option
of the set.
--
Michael
On 20 Nov 2020, at 01:33, Michael Paquier <michael@paquier.xyz> wrote:
This seems like a complication which brings little benefit since the only real
errorcase is OOM in creating the context? The built-in crypto support is
designed to never fail, and reading the OpenSSL code the only failure cases are
ENGINE initialization (which we don't do) and memory allocation. Did you
consider using EVP_MD_CTX_init instead which place the memory allocation
responsibility with the caller? Keeping this a void API and leaving the caller
to decide on memory allocation would make the API a lot simpler IMHO.Yes. That's actually the main take and why EVP callers *have* to let
OpenSSL do the allocation: we cannot know the size of EVP_MD_CTX in
advance in newer versions,
Yeah, there is that.
knowing that we still need to deal with the OOM failure handling
and pass down the error to the callers playing with SHA2, it feels
like the most consistent API to me for the frontend and the backend.
For the backend I'd prefer an API where the allocation worked like palloc, if
not then it's a straight exit through the giftshop. But if we want an API for
both the frontend and backend, I guess this is what we'll have to do.
+#ifndef _PG_CRYPTOHASH_H_ +#define _PG_CRYPTOHASH_H_ This should probably be CRYPTOHASH_H to be consistent?cryptohash.h sounds like a header file we could find somewhere else,
hence the extra PG_.
Ok, then at least I think we should use PG_CRYPTOHASH_H to be more consistent
with the tree, and since leading underscores in C are problematic spec-wise.
+/* Include internals of each implementation here */ +#include "sha2.c" Do we really want to implement this by including a .c file? Are there no other options you see?That was the least intrusive option I could figure out. Two other
options I have thought about:
- Compile those fallback implementations independently and publish the
internals in a separate header, but nobody is going to need that if we
have a generic entry point.
- Include the internals of each implementation in cryptohash.c, but
this bloats the file with more implementations added (HMAC and MD5
still need to be added on top of SHA2), and it messes up with the
existing copyright entries.
So splitting and just including them sounds like the cleanest option
of the set.
Personally I think the first option of using an internal header seems cleaner,
but MMV so I'll leave it to others to weigh in too.
cheers ./daniel
On Fri, Nov 20, 2020 at 11:17:44PM +0100, Daniel Gustafsson wrote:
On 20 Nov 2020, at 01:33, Michael Paquier <michael@paquier.xyz> wrote:
knowing that we still need to deal with the OOM failure handling
and pass down the error to the callers playing with SHA2, it feels
like the most consistent API to me for the frontend and the backend.For the backend I'd prefer an API where the allocation worked like palloc, if
not then it's a straight exit through the giftshop. But if we want an API for
both the frontend and backend, I guess this is what we'll have to do.
The fallback implementations can somewhat achieve that as we know all
the internals of the structures used.
Ok, then at least I think we should use PG_CRYPTOHASH_H to be more consistent
with the tree, and since leading underscores in C are problematic spec-wise.
Makes sense. Will fix.
That was the least intrusive option I could figure out. Two other
options I have thought about:
- Compile those fallback implementations independently and publish the
internals in a separate header, but nobody is going to need that if we
have a generic entry point.
- Include the internals of each implementation in cryptohash.c, but
this bloats the file with more implementations added (HMAC and MD5
still need to be added on top of SHA2), and it messes up with the
existing copyright entries.
So splitting and just including them sounds like the cleanest option
of the set.Personally I think the first option of using an internal header seems cleaner,
but MMV so I'll leave it to others to weigh in too.
What you meant and what I meant was slightly different here. I meant
publishing a header in src/include/common/ that would get installed,
and I'd rather avoid that. And you mean to have the header for local
consumption in src/common/. I would be fine with your third option as
well. Your suggestion is more consistent with what we do for the rest
of src/common/ and libpq actually. So I don't mind switching to
that.
--
Michael
On Sat, Nov 21, 2020 at 10:19:42AM +0900, Michael Paquier wrote:
What you meant and what I meant was slightly different here. I meant
publishing a header in src/include/common/ that would get installed,
and I'd rather avoid that. And you mean to have the header for local
consumption in src/common/. I would be fine with your third option as
well. Your suggestion is more consistent with what we do for the rest
of src/common/ and libpq actually. So I don't mind switching to
that.
I got to look at your suggestion, and finished with the attached which
is pretty close my previous set, except that MSVC scripts as well as
the header includes needed a slight refresh.
Please note that the OpenSSL docs tell that EVP_DigestInit() is
obsolete and that applications should just use EVP_DigestInit_ex(), so
I have kept the original:
https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestInit.html
The PG_CRYPTOHASH macro in cryptohash.h has been changed as you
suggested. What do you think?
--
Michael
Attachments:
v5-0001-Rework-SHA2-and-crypto-hash-APIs.patchtext/x-diff; charset=us-asciiDownload
From b4ec42146bfe8c9580c31d32a619e7712c519486 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Nov 2020 19:36:13 +0900
Subject: [PATCH v5 1/3] Rework SHA2 and crypto hash APIs
This will make easier a switch to EVP for the OpenSSL SHA2 layer. Note
that the layer introduced here is generalized for the purpose of a
future integration with HMAC, MD5, and even more.
---
src/include/common/checksum_helper.h | 13 +-
src/include/common/cryptohash.h | 40 ++++
src/include/common/scram-common.h | 17 +-
src/include/common/sha2.h | 89 +-------
src/include/replication/backup_manifest.h | 3 +-
src/backend/libpq/auth-scram.c | 94 +++++----
src/backend/replication/backup_manifest.c | 25 ++-
src/backend/replication/basebackup.c | 24 ++-
src/backend/utils/adt/cryptohashes.c | 53 +++--
src/common/Makefile | 6 +-
src/common/checksum_helper.c | 79 +++++--
src/common/cryptohash.c | 189 +++++++++++++++++
src/common/cryptohash_openssl.c | 196 ++++++++++++++++++
src/common/scram-common.c | 165 ++++++++++-----
src/common/sha2.c | 23 +-
.../common/sha2.h => common/sha2_int.h} | 38 +---
src/common/sha2_openssl.c | 102 ---------
src/bin/pg_verifybackup/parse_manifest.c | 15 +-
src/bin/pg_verifybackup/pg_verifybackup.c | 24 ++-
src/interfaces/libpq/fe-auth-scram.c | 114 +++++-----
contrib/pgcrypto/internal-sha2.c | 188 ++++-------------
src/tools/msvc/Mkvcbuild.pm | 3 +-
src/tools/pgindent/typedefs.list | 1 +
23 files changed, 928 insertions(+), 573 deletions(-)
create mode 100644 src/include/common/cryptohash.h
create mode 100644 src/common/cryptohash.c
create mode 100644 src/common/cryptohash_openssl.c
copy src/{include/common/sha2.h => common/sha2_int.h} (73%)
delete mode 100644 src/common/sha2_openssl.c
diff --git a/src/include/common/checksum_helper.h b/src/include/common/checksum_helper.h
index 48b0745dad..b07a34e7e4 100644
--- a/src/include/common/checksum_helper.h
+++ b/src/include/common/checksum_helper.h
@@ -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
index 0000000000..0e4a6631a3
--- /dev/null
+++ b/src/include/common/cryptohash.h
@@ -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 */
diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h
index 2edae2dd3c..f4a7c60725 100644
--- a/src/include/common/scram-common.h
+++ b/src/include/common/scram-common.h
@@ -13,6 +13,7 @@
#ifndef SCRAM_COMMON_H
#define SCRAM_COMMON_H
+#include "common/cryptohash.h"
#include "common/sha2.h"
/* Name of SCRAM mechanisms per IANA */
@@ -50,19 +51,19 @@
*/
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);
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index 9c4abf777d..c8b9096043 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -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
@@ -11,49 +12,9 @@
*-------------------------------------------------------------------------
*/
-/* $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
@@ -68,48 +29,4 @@
#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_ */
diff --git a/src/include/replication/backup_manifest.h b/src/include/replication/backup_manifest.h
index fb1291cbe4..e7c4047497 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_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
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 0f79b28bb5..5a54bdd353 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_cryptohash_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_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;
}
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index bab5e2f53b..c3f339c556 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -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;
}
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index b89df01fa7..0969036c7d 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -730,6 +730,7 @@ perform_base_backup(basebackup_options *opt)
}
/* clean up the resource owner we created */
+ FreeBackupManifest(&manifest);
WalSndResourceCleanup(true);
pgstat_progress_end_command();
@@ -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);
diff --git a/src/backend/utils/adt/cryptohashes.c b/src/backend/utils/adt/cryptohashes.c
index e897660927..5de294a7fd 100644
--- a/src/backend/utils/adt/cryptohashes.c
+++ b/src/backend/utils/adt/cryptohashes.c
@@ -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);
diff --git a/src/common/Makefile b/src/common/Makefile
index 25c55bd642..b8f5187282 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -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
diff --git a/src/common/checksum_helper.c b/src/common/checksum_helper.c
index 79a9a7447b..8e06524cd3 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_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
index 0000000000..f548497538
--- /dev/null
+++ b/src/common/cryptohash.c
@@ -0,0 +1,189 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.
+ */
+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
index 0000000000..406abf6163
--- /dev/null
+++ b/src/common/cryptohash_openssl.c
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.
+ */
+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);
+}
diff --git a/src/common/scram-common.c b/src/common/scram-common.c
index 4971134b22..04082414e5 100644
--- a/src/common/scram-common.c
+++ b/src/common/scram-common.c
@@ -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_cryptohash_ctx *sha256_ctx;
- pg_sha256_init(&sha256_ctx);
- pg_sha256_update(&sha256_ctx, key, keylen);
- pg_sha256_final(&sha256_ctx, keybuf);
+ 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,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_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 +268,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..1a462accc5 100644
--- a/src/common/sha2.c
+++ b/src/common/sha2.c
@@ -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
@@ -56,9 +57,19 @@
#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/include/common/sha2.h b/src/common/sha2_int.h
similarity index 73%
copy from src/include/common/sha2.h
copy to src/common/sha2_int.h
index 9c4abf777d..96db773f96 100644
--- a/src/include/common/sha2.h
+++ b/src/common/sha2_int.h
@@ -1,12 +1,12 @@
/*-------------------------------------------------------------------------
*
- * sha2.h
- * Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
+ * 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/include/common/sha2.h
+ * src/common/sha2_int.h
*
*-------------------------------------------------------------------------
*/
@@ -47,34 +47,11 @@
* $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
-#ifndef _PG_SHA2_H_
-#define _PG_SHA2_H_
+#ifndef PG_SHA2_INT_H
+#define PG_SHA2_INT_H
-#ifdef USE_OPENSSL
-#include <openssl/sha.h>
-#endif
+#include "common/sha2.h"
-/*** SHA224/256/384/512 Various Length Definitions ***********************/
-#define PG_SHA224_BLOCK_LENGTH 64
-#define PG_SHA224_DIGEST_LENGTH 28
-#define PG_SHA224_DIGEST_STRING_LENGTH (PG_SHA224_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA256_BLOCK_LENGTH 64
-#define PG_SHA256_DIGEST_LENGTH 32
-#define PG_SHA256_DIGEST_STRING_LENGTH (PG_SHA256_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA384_BLOCK_LENGTH 128
-#define PG_SHA384_DIGEST_LENGTH 48
-#define PG_SHA384_DIGEST_STRING_LENGTH (PG_SHA384_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA512_BLOCK_LENGTH 128
-#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];
@@ -89,7 +66,6 @@ typedef struct pg_sha512_ctx
} 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);
@@ -112,4 +88,4 @@ 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_ */
+#endif /* PG_SHA2_INT_H */
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
deleted file mode 100644
index 41673b3a88..0000000000
--- a/src/common/sha2_openssl.c
+++ /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);
-}
diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c
index 608e23538b..5b4ce28837 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_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);
}
/*
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..0fe53e15af 100644
--- a/contrib/pgcrypto/internal-sha2.c
+++ b/contrib/pgcrypto/internal-sha2.c
@@ -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);
}
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 90594bd41b..720b55142b 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -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');
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index fde701bfd4..9c959e126d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3180,6 +3180,7 @@ pg_conn_host_type
pg_conv_map
pg_crc32
pg_crc32c
+pg_cryptohash_ctx
pg_ctype_cache
pg_enc
pg_enc2gettext
--
2.29.2
v5-0002-Switch-cryptohash_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From a0abdf1da5ebb468d4b693f5feafce6db97712e6 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Nov 2020 19:43:07 +0900
Subject: [PATCH v5 2/3] Switch cryptohash_openssl.c to use EVP
Postgres is two decades late for this switch.
---
src/include/utils/resowner_private.h | 7 +++
src/backend/utils/resowner/resowner.c | 65 ++++++++++++++++++++
src/common/cryptohash_openssl.c | 88 +++++++++++++--------------
3 files changed, 113 insertions(+), 47 deletions(-)
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..5ce6fcf882 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
Datum handle);
+/* support for EVP context management */
+extern void ResourceOwnerEnlargeEVP(ResourceOwner owner);
+extern void ResourceOwnerRememberEVP(ResourceOwner owner,
+ Datum handle);
+extern void ResourceOwnerForgetEVP(ResourceOwner owner,
+ Datum handle);
+
#endif /* RESOWNER_PRIVATE_H */
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..1efb5e98b4 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,10 @@
*/
#include "postgres.h"
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
@@ -128,6 +132,7 @@ typedef struct ResourceOwnerData
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray evparr; /* EVP contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
@@ -175,6 +180,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintEVPLeakWarning(Datum handle);
/*****************************************************************************
@@ -444,6 +450,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->evparr), PointerGetDatum(NULL));
return owner;
}
@@ -553,6 +560,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
jit_release_context(context);
}
+
+ /* Ditto for EVP contexts */
+ while (ResourceArrayGetAny(&(owner->evparr), &foundres))
+ {
+ if (isCommit)
+ PrintEVPLeakWarning(foundres);
+#ifdef USE_OPENSSL
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) DatumGetPointer(foundres));
+#endif
+ ResourceOwnerForgetEVP(owner, foundres);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
@@ -725,6 +743,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->evparr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
@@ -752,6 +771,7 @@ ResourceOwnerDelete(ResourceOwner owner)
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->evparr));
pfree(owner);
}
@@ -1370,3 +1390,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * EVP context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeEVP(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->evparr));
+}
+
+/*
+ * Remember that an EVP context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeEVP()
+ */
+void
+ResourceOwnerRememberEVP(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->evparr), handle);
+}
+
+/*
+ * Forget that an EVP context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetEVP(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->evparr), handle))
+ elog(ERROR, "EVP context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintEVPLeakWarning(Datum handle)
+{
+ elog(WARNING, "EVP context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 406abf6163..cde3c9d597 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -21,9 +21,14 @@
#include "postgres_fe.h"
#endif
-#include <openssl/sha.h>
+#include <openssl/evp.h>
#include "common/cryptohash.h"
+#ifndef FRONTEND
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
@@ -53,25 +58,31 @@ pg_cryptohash_create(pg_cryptohash_type type)
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;
- }
+#ifndef FRONTEND
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ ctx->data = EVP_MD_CTX_create();
if (ctx->data == NULL)
{
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
+#else
FREE(ctx);
return NULL;
+#endif
}
+#ifndef FRONTEND
+ ResourceOwnerRememberEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
return ctx;
}
@@ -91,16 +102,20 @@ pg_cryptohash_init(pg_cryptohash_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;
}
@@ -123,21 +138,7 @@ pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
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)
@@ -158,21 +159,7 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
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)
@@ -190,7 +177,14 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(ctx->data));
+#endif
+
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
}
--
2.29.2
v5-0003-Move-pgcrypto-to-use-in-core-resowner-facility-fo.patchtext/x-diff; charset=us-asciiDownload
From f0c79aee4dd94ea25c98387157c7aa2d4949337b Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Nov 2020 19:43:37 +0900
Subject: [PATCH v5 3/3] Move pgcrypto to use in-core resowner facility for EVP
This simplifies some code in pgcrypto as it does not need anymore to
rely on its own internal logic for the EVP registration and automatic
cleanup.
---
contrib/pgcrypto/openssl.c | 87 +++++---------------------------------
1 file changed, 10 insertions(+), 77 deletions(-)
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 5ebe213406..d8e1269a40 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -38,6 +38,7 @@
#include "px.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
+#include "utils/resowner_private.h"
/*
* Max lengths we might want to handle.
@@ -49,67 +50,12 @@
* Hashes
*/
-/*
- * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
- * objects in a linked list, allocated in TopMemoryContext. We use the
- * ResourceOwner mechanism to free them on abort.
- */
typedef struct OSSLDigest
{
const EVP_MD *algo;
EVP_MD_CTX *ctx;
-
- ResourceOwner owner;
- struct OSSLDigest *next;
- struct OSSLDigest *prev;
} OSSLDigest;
-static OSSLDigest *open_digests = NULL;
-static bool digest_resowner_callback_registered = false;
-
-static void
-free_openssl_digest(OSSLDigest *digest)
-{
- EVP_MD_CTX_destroy(digest->ctx);
- if (digest->prev)
- digest->prev->next = digest->next;
- else
- open_digests = digest->next;
- if (digest->next)
- digest->next->prev = digest->prev;
- pfree(digest);
-}
-
-/*
- * Close any open OpenSSL handles on abort.
- */
-static void
-digest_free_callback(ResourceReleasePhase phase,
- bool isCommit,
- bool isTopLevel,
- void *arg)
-{
- OSSLDigest *curr;
- OSSLDigest *next;
-
- if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
- return;
-
- next = open_digests;
- while (next)
- {
- curr = next;
- next = curr->next;
-
- if (curr->owner == CurrentResourceOwner)
- {
- if (isCommit)
- elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
- free_openssl_digest(curr);
- }
- }
-}
-
static unsigned
digest_result_size(PX_MD *h)
{
@@ -155,7 +101,10 @@ digest_free(PX_MD *h)
{
OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
- free_openssl_digest(digest);
+ EVP_MD_CTX_destroy(digest->ctx);
+ ResourceOwnerForgetEVP(CurrentResourceOwner,
+ PointerGetDatum(digest->ctx));
+ pfree(digest);
pfree(h);
}
@@ -177,42 +126,26 @@ px_find_digest(const char *name, PX_MD **res)
OpenSSL_add_all_algorithms();
}
- if (!digest_resowner_callback_registered)
- {
- RegisterResourceReleaseCallback(digest_free_callback, NULL);
- digest_resowner_callback_registered = true;
- }
-
md = EVP_get_digestbyname(name);
if (md == NULL)
return PXE_NO_HASH;
- /*
- * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
- * The order is crucial, to make sure we don't leak anything on
- * out-of-memory or other error.
- */
- digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest));
-
ctx = EVP_MD_CTX_create();
if (!ctx)
- {
- pfree(digest);
return -1;
- }
+
if (EVP_DigestInit_ex(ctx, md, NULL) == 0)
{
EVP_MD_CTX_destroy(ctx);
- pfree(digest);
return -1;
}
+ ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+ ResourceOwnerRememberEVP(CurrentResourceOwner, PointerGetDatum(ctx));
+
+ digest = palloc(sizeof(*digest));
digest->algo = md;
digest->ctx = ctx;
- digest->owner = CurrentResourceOwner;
- digest->next = open_digests;
- digest->prev = NULL;
- open_digests = digest;
/* The PX_MD object is allocated in the current memory context. */
h = palloc(sizeof(*h));
--
2.29.2
On 24 Nov 2020, at 11:52, Michael Paquier <michael@paquier.xyz> wrote:
I got to look at your suggestion, and finished with the attached which
is pretty close my previous set, except that MSVC scripts as well as
the header includes needed a slight refresh.
Looks good, nothing major sticks out. I'm not excited about all the
allocations needed here now, but it seems the only optipn.
Please note that the OpenSSL docs tell that EVP_DigestInit() is
obsolete and that applications should just use EVP_DigestInit_ex(), so
I have kept the original:
https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestInit.html
Fair enough.
What do you think?
A few comments in no particular order:
+ if (scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN) < 0 ||
+ scram_HMAC_update(&ctx,
+ state->client_first_message_bare,
..snip..
+ scram_HMAC_final(ServerSignature, &ctx) < 0)
+ {
+ elog(ERROR, "could not calculate server signature");
+ }
Some of these long if-statements omit the {} around the elog, while some (like
this one) don't. I think it makes sense from a readability POV to use the
braces.
+ * pg_cryptohash_create
+ *
+ * Allocate a hash context. Returns NULL on failure.
This comment should perhaps be amended to specify that it returns NULL on
failure in the frontend, in the backend it won't return on error.
+ 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;
Would codepaths like these become more readable if pg_cryptohash_ctx used a
union with shaxxx_ctx's rather than a void pointer for the data part?
+ /* Ditto for EVP contexts */
+ while (ResourceArrayGetAny(&(owner->evparr), &foundres))
+ {
+ if (isCommit)
+ PrintEVPLeakWarning(foundres);
+#ifdef USE_OPENSSL
+ EVP_MD_CTX_destroy((EVP_MD_CTX *) DatumGetPointer(foundres));
+#endif
+ ResourceOwnerForgetEVP(owner, foundres);
+ }
Since the cryptohash support is now generalized behind an abstraction layer,
wouldn't it make sense to roll the resource ownership there as well kind of
like how JIT is handled? It would make it easier to implement TLS backend
support, and we won't have to inject OpenSSL headers here.
cheers ./daniel
On Mon, Nov 30, 2020 at 01:43:24PM +0100, Daniel Gustafsson wrote:
Looks good, nothing major sticks out.
Thanks for the review.
I'm not excited about all the allocations needed here now, but it
seems the only option.
Yeah, OpenSSL forces a bit our hand here I am afraid..
Some of these long if-statements omit the {} around the elog, while some (like
this one) don't. I think it makes sense from a readability POV to use the
braces.
Agreed.
+ * pg_cryptohash_create + * + * Allocate a hash context. Returns NULL on failure. This comment should perhaps be amended to specify that it returns NULL on failure in the frontend, in the backend it won't return on error.
Okay. Let's be precise.
+ 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; Would codepaths like these become more readable if pg_cryptohash_ctx used a union with shaxxx_ctx's rather than a void pointer for the data part?
Hmm. I am not sure that this would help much, because we'd still need
to cast to the local structures in this case as the pg_shaXXX_ctx are
local to sha2.c, and we still need to keep some void* in
cryptohash.h.
Since the cryptohash support is now generalized behind an abstraction layer,
wouldn't it make sense to roll the resource ownership there as well kind of
like how JIT is handled? It would make it easier to implement TLS backend
support, and we won't have to inject OpenSSL headers here.
So, you are referring here about using a new API in the abstraction
layer. This makes sense. What about naming that
pg_cryptohash_context_free(void *)?
--
Michael
On 30 Nov 2020, at 14:13, Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Nov 30, 2020 at 01:43:24PM +0100, Daniel Gustafsson wrote:
Since the cryptohash support is now generalized behind an abstraction layer,
wouldn't it make sense to roll the resource ownership there as well kind of
like how JIT is handled? It would make it easier to implement TLS backend
support, and we won't have to inject OpenSSL headers here.So, you are referring here about using a new API in the abstraction
layer. This makes sense. What about naming that
pg_cryptohash_context_free(void *)?
Yeah, that's along the lines of what I was thinking of.
cheers ./daniel
On Mon, Nov 30, 2020 at 02:29:29PM +0100, Daniel Gustafsson wrote:
Yeah, that's along the lines of what I was thinking of.
Hmm. I have looked at that, and thought first about having directly a
reference to the resowner directly in pg_cryptohash_ctx, but that's
not a good plan for two reasons:
- common/cryptohash.h would get knowledge of that, requiring bundling
in a bunch of dependencies.
- There is no need for that in the non-OpenSSL case.
So, instead, I have been thinking about using an extra context layer
only for cryptohash_openssl.c with a structure saved as
pg_cryptohash_context->data that stores the information about
EVP_MD_CTX* and the resource owner. Then, I was thinking about
storing directly pg_cryptohash_ctx in the resowner EVP array and just
call pg_cryptohash_free() from resowner.c without the need of an
extra routine. I have not tested this idea but that should work.
What's your take?
In parallel, I have spent more time today polishing and reviewing 0001
(indented, adjusted a couple of areas and added also brackets and
extra comments as you suggested) and tested it on Linux and Windows,
with and without OpenSSL down to 1.0.1, the oldest version supported
on HEAD. So I'd like to apply the attached first and sort out the
resowner stuff in a next step.
--
Michael
Attachments:
v6-0001-Rework-SHA2-and-crypto-hash-APIs.patchtext/x-diff; charset=us-asciiDownload
From 5ffd653336f1c41043635fad3618cf0bae7b1cec Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 1 Dec 2020 13:21:44 +0900
Subject: [PATCH v6] Rework SHA2 and crypto hash APIs
This will make easier a switch to EVP for the OpenSSL SHA2 layer. Note
that the layer introduced here is generalized for the purpose of a
future integration with HMAC, MD5, and even more.
---
src/include/common/checksum_helper.h | 13 +-
src/include/common/cryptohash.h | 40 ++++
src/include/common/scram-common.h | 17 +-
src/include/common/sha2.h | 89 +-------
src/include/replication/backup_manifest.h | 3 +-
src/backend/libpq/auth-scram.c | 113 ++++++----
src/backend/replication/backup_manifest.c | 25 ++-
src/backend/replication/basebackup.c | 24 ++-
src/backend/utils/adt/cryptohashes.c | 53 +++--
src/common/Makefile | 6 +-
src/common/checksum_helper.c | 79 +++++--
src/common/cryptohash.c | 190 +++++++++++++++++
src/common/cryptohash_openssl.c | 197 ++++++++++++++++++
src/common/scram-common.c | 173 ++++++++++-----
src/common/sha2.c | 23 +-
.../common/sha2.h => common/sha2_int.h} | 38 +---
src/common/sha2_openssl.c | 102 ---------
src/bin/pg_verifybackup/parse_manifest.c | 15 +-
src/bin/pg_verifybackup/pg_verifybackup.c | 24 ++-
src/interfaces/libpq/fe-auth-scram.c | 118 ++++++-----
contrib/pgcrypto/internal-sha2.c | 188 ++++-------------
src/tools/msvc/Mkvcbuild.pm | 3 +-
src/tools/pgindent/typedefs.list | 2 +
23 files changed, 955 insertions(+), 580 deletions(-)
create mode 100644 src/include/common/cryptohash.h
create mode 100644 src/common/cryptohash.c
create mode 100644 src/common/cryptohash_openssl.c
copy src/{include/common/sha2.h => common/sha2_int.h} (73%)
delete mode 100644 src/common/sha2_openssl.c
diff --git a/src/include/common/checksum_helper.h b/src/include/common/checksum_helper.h
index 48b0745dad..b07a34e7e4 100644
--- a/src/include/common/checksum_helper.h
+++ b/src/include/common/checksum_helper.h
@@ -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
index 0000000000..0e4a6631a3
--- /dev/null
+++ b/src/include/common/cryptohash.h
@@ -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 */
diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h
index 2edae2dd3c..f4a7c60725 100644
--- a/src/include/common/scram-common.h
+++ b/src/include/common/scram-common.h
@@ -13,6 +13,7 @@
#ifndef SCRAM_COMMON_H
#define SCRAM_COMMON_H
+#include "common/cryptohash.h"
#include "common/sha2.h"
/* Name of SCRAM mechanisms per IANA */
@@ -50,19 +51,19 @@
*/
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);
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index 9c4abf777d..c8b9096043 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -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
@@ -11,49 +12,9 @@
*-------------------------------------------------------------------------
*/
-/* $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
@@ -68,48 +29,4 @@
#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_ */
diff --git a/src/include/replication/backup_manifest.h b/src/include/replication/backup_manifest.h
index fb1291cbe4..e7c4047497 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_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
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 0f79b28bb5..6879a81618 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -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;
}
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index bab5e2f53b..c3f339c556 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -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;
}
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index b89df01fa7..22be7ca9d5 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -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);
diff --git a/src/backend/utils/adt/cryptohashes.c b/src/backend/utils/adt/cryptohashes.c
index e897660927..5de294a7fd 100644
--- a/src/backend/utils/adt/cryptohashes.c
+++ b/src/backend/utils/adt/cryptohashes.c
@@ -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);
diff --git a/src/common/Makefile b/src/common/Makefile
index 25c55bd642..b8f5187282 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -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
diff --git a/src/common/checksum_helper.c b/src/common/checksum_helper.c
index 79a9a7447b..8e06524cd3 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_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
index 0000000000..a61091f456
--- /dev/null
+++ b/src/common/cryptohash.c
@@ -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
index 0000000000..8e2c69b48b
--- /dev/null
+++ b/src/common/cryptohash_openssl.c
@@ -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);
+}
diff --git a/src/common/scram-common.c b/src/common/scram-common.c
index 4971134b22..caab68926d 100644
--- a/src/common/scram-common.c
+++ b/src/common/scram-common.c
@@ -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_cryptohash_ctx *sha256_ctx;
- pg_sha256_init(&sha256_ctx);
- pg_sha256_update(&sha256_ctx, key, keylen);
- pg_sha256_final(&sha256_ctx, keybuf);
+ 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:
diff --git a/src/common/sha2.c b/src/common/sha2.c
index 0d329bb238..1a462accc5 100644
--- a/src/common/sha2.c
+++ b/src/common/sha2.c
@@ -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
@@ -56,9 +57,19 @@
#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/include/common/sha2.h b/src/common/sha2_int.h
similarity index 73%
copy from src/include/common/sha2.h
copy to src/common/sha2_int.h
index 9c4abf777d..96db773f96 100644
--- a/src/include/common/sha2.h
+++ b/src/common/sha2_int.h
@@ -1,12 +1,12 @@
/*-------------------------------------------------------------------------
*
- * sha2.h
- * Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
+ * 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/include/common/sha2.h
+ * src/common/sha2_int.h
*
*-------------------------------------------------------------------------
*/
@@ -47,34 +47,11 @@
* $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
-#ifndef _PG_SHA2_H_
-#define _PG_SHA2_H_
+#ifndef PG_SHA2_INT_H
+#define PG_SHA2_INT_H
-#ifdef USE_OPENSSL
-#include <openssl/sha.h>
-#endif
+#include "common/sha2.h"
-/*** SHA224/256/384/512 Various Length Definitions ***********************/
-#define PG_SHA224_BLOCK_LENGTH 64
-#define PG_SHA224_DIGEST_LENGTH 28
-#define PG_SHA224_DIGEST_STRING_LENGTH (PG_SHA224_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA256_BLOCK_LENGTH 64
-#define PG_SHA256_DIGEST_LENGTH 32
-#define PG_SHA256_DIGEST_STRING_LENGTH (PG_SHA256_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA384_BLOCK_LENGTH 128
-#define PG_SHA384_DIGEST_LENGTH 48
-#define PG_SHA384_DIGEST_STRING_LENGTH (PG_SHA384_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA512_BLOCK_LENGTH 128
-#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];
@@ -89,7 +66,6 @@ typedef struct pg_sha512_ctx
} 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);
@@ -112,4 +88,4 @@ 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_ */
+#endif /* PG_SHA2_INT_H */
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
deleted file mode 100644
index 41673b3a88..0000000000
--- a/src/common/sha2_openssl.c
+++ /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);
-}
diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c
index 608e23538b..5b4ce28837 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_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);
}
/*
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..6dcf574f62 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,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);
-
- 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..0fe53e15af 100644
--- a/contrib/pgcrypto/internal-sha2.c
+++ b/contrib/pgcrypto/internal-sha2.c
@@ -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);
}
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 90594bd41b..720b55142b 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -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');
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b8ca8cffd9..04464c2e76 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -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
--
2.29.2
On 1 Dec 2020, at 06:38, Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Nov 30, 2020 at 02:29:29PM +0100, Daniel Gustafsson wrote:
Yeah, that's along the lines of what I was thinking of.
Hmm. I have looked at that, and thought first about having directly a
reference to the resowner directly in pg_cryptohash_ctx, but that's
not a good plan for two reasons:
- common/cryptohash.h would get knowledge of that, requiring bundling
in a bunch of dependencies.
- There is no need for that in the non-OpenSSL case.
So, instead, I have been thinking about using an extra context layer
only for cryptohash_openssl.c with a structure saved as
pg_cryptohash_context->data that stores the information about
EVP_MD_CTX* and the resource owner. Then, I was thinking about
storing directly pg_cryptohash_ctx in the resowner EVP array and just
call pg_cryptohash_free() from resowner.c without the need of an
extra routine. I have not tested this idea but that should work.
What's your take?
That sounds like it would work. Since the cryptohash implementation owns and
controls the void *data contents it can store whatever it needs to properly
free it.
In parallel, I have spent more time today polishing and reviewing 0001
(indented, adjusted a couple of areas and added also brackets and
extra comments as you suggested) and tested it on Linux and Windows,
with and without OpenSSL down to 1.0.1, the oldest version supported
on HEAD. So I'd like to apply the attached first and sort out the
resowner stuff in a next step.
+1 on separating the API, EVP migration, resowner patches.
Reading through the v6 patch I see nothing sticking out and all review comments
addressed, +1 on applying that one and then we'll take if from there with the
remaining ones in the patchset.
cheers ./daniel
On Tue, Dec 01, 2020 at 10:10:45AM +0100, Daniel Gustafsson wrote:
That sounds like it would work. Since the cryptohash implementation owns and
controls the void *data contents it can store whatever it needs to properly
free it.
That seems to work properly. I have injected some elog(ERROR) with
the attached in some of the SQL functions from cryptohashes.c and the
cleanup happens with the correct resowner references. It felt a bit
strange to use directly the term EVP in resowner.c once I did this
switch, so this is renamed to "CryptoHash" instead.
Reading through the v6 patch I see nothing sticking out and all review comments
addressed, +1 on applying that one and then we'll take if from there with the
remaining ones in the patchset.
Thanks. 0001 has been applied and the buildfarm does not complain, so
it looks like we are good (I'll take care of any issues, like the one
Fujii-san has just reported). Attached are new patches for 0002, the
EVP switch. One thing I noticed is that we need to free the backup
manifest a bit earlier once we begin to use resource owner in
basebackup.c as there is a specific step that may do a double-free.
This would not happen when not using OpenSSL or on HEAD. It would be
easy to separate the resowner and cryptohash portions of the patch
here, but both are tightly linked, so I'd prefer to keep them
together.
Another thing to note is that this makes 0003 for pgcrypto
meaningless, because this would track down only states created by
cryptohash_openssl.c, and not directly EVP_MD_CTX. Consistency in the
backend code is for the best, and considering that pgcrypto has
similar linked lists for ciphers it feels a bit weird to switch only
half of it to use something in resowner.c. So, I am not sure if we
need to do anything here, and I am discarding this part.
--
Michael
Attachments:
v7-0001-Switch-cryptohash_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From d2c9f2a9c9a36d80a014d588334e21a709a1c27d Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 2 Dec 2020 11:51:43 +0900
Subject: [PATCH v7] Switch cryptohash_openssl.c to use EVP
Postgres is two decades late for this switch.
---
src/include/utils/resowner_private.h | 7 ++
src/backend/replication/basebackup.c | 8 +-
src/backend/utils/resowner/resowner.c | 62 ++++++++++++
src/common/cryptohash_openssl.c | 132 ++++++++++++++++----------
src/tools/pgindent/typedefs.list | 1 +
5 files changed, 158 insertions(+), 52 deletions(-)
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..c373788bc1 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
Datum handle);
+/* support for cryptohash context management */
+extern void ResourceOwnerEnlargeCryptoHash(ResourceOwner owner);
+extern void ResourceOwnerRememberCryptoHash(ResourceOwner owner,
+ Datum handle);
+extern void ResourceOwnerForgetCryptoHash(ResourceOwner owner,
+ Datum handle);
+
#endif /* RESOWNER_PRIVATE_H */
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 22be7ca9d5..79b458c185 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -729,11 +729,17 @@ perform_base_backup(basebackup_options *opt)
errmsg("checksum verification failure during base backup")));
}
+ /*
+ * Make sure to free the manifest before the resource owners as
+ * manifests use cryptohash contexts that may depend on resource
+ * owners (like OpenSSL).
+ */
+ FreeBackupManifest(&manifest);
+
/* clean up the resource owner we created */
WalSndResourceCleanup(true);
pgstat_progress_end_command();
- FreeBackupManifest(&manifest);
}
/*
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..546ad8d1c5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
*/
#include "postgres.h"
+#include "common/cryptohash.h"
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
@@ -128,6 +129,7 @@ typedef struct ResourceOwnerData
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray cryptohasharr; /* cryptohash contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
@@ -175,6 +177,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintCryptoHashLeakWarning(Datum handle);
/*****************************************************************************
@@ -444,6 +447,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
return owner;
}
@@ -553,6 +557,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
jit_release_context(context);
}
+
+ /* Ditto for cryptohash contexts */
+ while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
+ {
+ pg_cryptohash_ctx *context =
+ (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+
+ if (isCommit)
+ PrintCryptoHashLeakWarning(foundres);
+ pg_cryptohash_free(context);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
@@ -725,6 +740,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->cryptohasharr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
@@ -752,6 +768,7 @@ ResourceOwnerDelete(ResourceOwner owner)
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->cryptohasharr));
pfree(owner);
}
@@ -1370,3 +1387,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * cryptohash context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->cryptohasharr));
+}
+
+/*
+ * Remember that a cryptohash context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeCryptoHash()
+ */
+void
+ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->cryptohasharr), handle);
+}
+
+/*
+ * Forget that a cryptohash context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->cryptohasharr), handle))
+ elog(ERROR, "cryptohash context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintCryptoHashLeakWarning(Datum handle)
+{
+ elog(WARNING, "cryptohash context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 8e2c69b48b..d0f58b81be 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -21,9 +21,14 @@
#include "postgres_fe.h"
#endif
-#include <openssl/sha.h>
+#include <openssl/evp.h>
#include "common/cryptohash.h"
+#ifndef FRONTEND
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
@@ -37,6 +42,21 @@
#define FREE(ptr) free(ptr)
#endif
+/*
+ * Internal structure for pg_cryptohash_ctx->data.
+ *
+ * This tracks the resource owner associated to each EVP context data
+ * for the backend.
+ */
+typedef struct pg_cryptohash_state
+{
+ EVP_MD_CTX *evpctx;
+
+#ifndef FRONTEND
+ ResourceOwner resowner;
+#endif
+} pg_cryptohash_state;
+
/*
* pg_cryptohash_create
*
@@ -47,32 +67,51 @@ pg_cryptohash_ctx *
pg_cryptohash_create(pg_cryptohash_type type)
{
pg_cryptohash_ctx *ctx;
+ pg_cryptohash_state *state;
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)
+ state = ALLOC(sizeof(pg_cryptohash_state));
+ if (state == NULL)
{
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
return NULL;
}
+ ctx->data = state;
+ ctx->type = type;
+
+#ifndef FRONTEND
+ ResourceOwnerEnlargeCryptoHash(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ state->evpctx = EVP_MD_CTX_create();
+
+ if (state->evpctx == NULL)
+ {
+ explicit_bzero(state, sizeof(pg_cryptohash_state));
+ explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
+#else
+ FREE(state);
+ FREE(ctx);
+ return NULL;
+#endif
+ }
+
+#ifndef FRONTEND
+ state->resowner = CurrentResourceOwner;
+ ResourceOwnerRememberCryptoHash(CurrentResourceOwner,
+ PointerGetDatum(ctx));
+#endif
+
return ctx;
}
@@ -85,23 +124,26 @@ int
pg_cryptohash_init(pg_cryptohash_ctx *ctx)
{
int status = 0;
+ pg_cryptohash_state *state;
if (ctx == NULL)
return 0;
+ state = (pg_cryptohash_state *) ctx->data;
+
switch (ctx->type)
{
case PG_SHA224:
- status = SHA224_Init((SHA256_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha224(), NULL);
break;
case PG_SHA256:
- status = SHA256_Init((SHA256_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha256(), NULL);
break;
case PG_SHA384:
- status = SHA384_Init((SHA512_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha384(), NULL);
break;
case PG_SHA512:
- status = SHA512_Init((SHA512_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha512(), NULL);
break;
}
@@ -120,25 +162,13 @@ int
pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
{
int status;
+ pg_cryptohash_state *state;
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;
- }
+ state = (pg_cryptohash_state *) ctx->data;
+ status = EVP_DigestUpdate(state->evpctx, data, len);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
@@ -155,25 +185,13 @@ int
pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
{
int status;
+ pg_cryptohash_state *state;
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;
- }
+ state = (pg_cryptohash_state *) ctx->data;
+ status = EVP_DigestFinal_ex(state->evpctx, dest, 0);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
@@ -189,9 +207,21 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
void
pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
+ pg_cryptohash_state *state;
+
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ state = (pg_cryptohash_state *) ctx->data;
+ EVP_MD_CTX_destroy(state->evpctx);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetCryptoHash(state->resowner,
+ PointerGetDatum(ctx));
+#endif
+
+ explicit_bzero(state, sizeof(pg_cryptohash_state));
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+ FREE(state);
FREE(ctx);
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 04464c2e76..cf63acbf6f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3180,6 +3180,7 @@ pg_conv_map
pg_crc32
pg_crc32c
pg_cryptohash_ctx
+pg_cryptohash_state
pg_cryptohash_type
pg_ctype_cache
pg_enc
--
2.29.2
On Wed, Dec 02, 2020 at 12:03:49PM +0900, Michael Paquier wrote:
Thanks. 0001 has been applied and the buildfarm does not complain, so
it looks like we are good (I'll take care of any issues, like the one
Fujii-san has just reported). Attached are new patches for 0002, the
EVP switch. One thing I noticed is that we need to free the backup
manifest a bit earlier once we begin to use resource owner in
basebackup.c as there is a specific step that may do a double-free.
This would not happen when not using OpenSSL or on HEAD. It would be
easy to separate the resowner and cryptohash portions of the patch
here, but both are tightly linked, so I'd prefer to keep them
together.
Attached is a rebased version to take care of the conflicts introduced
by 91624c2f.
--
Michael
Attachments:
v8-0001-Switch-cryptohash_openssl.c-to-use-EVP.patchtext/x-diff; charset=us-asciiDownload
From a68819b3f843b4ce2883c35c008d5dd4fb47ee35 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 2 Dec 2020 11:51:43 +0900
Subject: [PATCH v8] Switch cryptohash_openssl.c to use EVP
Postgres is two decades late for this switch.
---
src/include/utils/resowner_private.h | 7 ++
src/backend/replication/basebackup.c | 8 +-
src/backend/utils/resowner/resowner.c | 62 ++++++++++++
src/common/cryptohash_openssl.c | 132 ++++++++++++++++----------
src/tools/pgindent/typedefs.list | 1 +
5 files changed, 158 insertions(+), 52 deletions(-)
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..c373788bc1 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
Datum handle);
+/* support for cryptohash context management */
+extern void ResourceOwnerEnlargeCryptoHash(ResourceOwner owner);
+extern void ResourceOwnerRememberCryptoHash(ResourceOwner owner,
+ Datum handle);
+extern void ResourceOwnerForgetCryptoHash(ResourceOwner owner,
+ Datum handle);
+
#endif /* RESOWNER_PRIVATE_H */
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 22be7ca9d5..79b458c185 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -729,11 +729,17 @@ perform_base_backup(basebackup_options *opt)
errmsg("checksum verification failure during base backup")));
}
+ /*
+ * Make sure to free the manifest before the resource owners as
+ * manifests use cryptohash contexts that may depend on resource
+ * owners (like OpenSSL).
+ */
+ FreeBackupManifest(&manifest);
+
/* clean up the resource owner we created */
WalSndResourceCleanup(true);
pgstat_progress_end_command();
- FreeBackupManifest(&manifest);
}
/*
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..546ad8d1c5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
*/
#include "postgres.h"
+#include "common/cryptohash.h"
#include "common/hashfn.h"
#include "jit/jit.h"
#include "storage/bufmgr.h"
@@ -128,6 +129,7 @@ typedef struct ResourceOwnerData
ResourceArray filearr; /* open temporary files */
ResourceArray dsmarr; /* dynamic shmem segments */
ResourceArray jitarr; /* JIT contexts */
+ ResourceArray cryptohasharr; /* cryptohash contexts */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
@@ -175,6 +177,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);
static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintCryptoHashLeakWarning(Datum handle);
/*****************************************************************************
@@ -444,6 +447,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+ ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
return owner;
}
@@ -553,6 +557,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
jit_release_context(context);
}
+
+ /* Ditto for cryptohash contexts */
+ while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
+ {
+ pg_cryptohash_ctx *context =
+ (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+
+ if (isCommit)
+ PrintCryptoHashLeakWarning(foundres);
+ pg_cryptohash_free(context);
+ }
}
else if (phase == RESOURCE_RELEASE_LOCKS)
{
@@ -725,6 +740,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->filearr.nitems == 0);
Assert(owner->dsmarr.nitems == 0);
Assert(owner->jitarr.nitems == 0);
+ Assert(owner->cryptohasharr.nitems == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
/*
@@ -752,6 +768,7 @@ ResourceOwnerDelete(ResourceOwner owner)
ResourceArrayFree(&(owner->filearr));
ResourceArrayFree(&(owner->dsmarr));
ResourceArrayFree(&(owner->jitarr));
+ ResourceArrayFree(&(owner->cryptohasharr));
pfree(owner);
}
@@ -1370,3 +1387,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
elog(ERROR, "JIT context %p is not owned by resource owner %s",
DatumGetPointer(handle), owner->name);
}
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * cryptohash context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
+{
+ ResourceArrayEnlarge(&(owner->cryptohasharr));
+}
+
+/*
+ * Remember that a cryptohash context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeCryptoHash()
+ */
+void
+ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
+{
+ ResourceArrayAdd(&(owner->cryptohasharr), handle);
+}
+
+/*
+ * Forget that a cryptohash context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
+{
+ if (!ResourceArrayRemove(&(owner->cryptohasharr), handle))
+ elog(ERROR, "cryptohash context %p is not owned by resource owner %s",
+ DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintCryptoHashLeakWarning(Datum handle)
+{
+ elog(WARNING, "cryptohash context reference leak: context %p still referenced",
+ DatumGetPointer(handle));
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 33f17cac33..3c88ae648e 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -21,9 +21,14 @@
#include "postgres_fe.h"
#endif
-#include <openssl/sha.h>
+#include <openssl/evp.h>
#include "common/cryptohash.h"
+#ifndef FRONTEND
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/resowner_private.h"
+#endif
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
@@ -37,6 +42,21 @@
#define FREE(ptr) free(ptr)
#endif
+/*
+ * Internal structure for pg_cryptohash_ctx->data.
+ *
+ * This tracks the resource owner associated to each EVP context data
+ * for the backend.
+ */
+typedef struct pg_cryptohash_state
+{
+ EVP_MD_CTX *evpctx;
+
+#ifndef FRONTEND
+ ResourceOwner resowner;
+#endif
+} pg_cryptohash_state;
+
/*
* pg_cryptohash_create
*
@@ -47,32 +67,51 @@ pg_cryptohash_ctx *
pg_cryptohash_create(pg_cryptohash_type type)
{
pg_cryptohash_ctx *ctx;
+ pg_cryptohash_state *state;
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)
+ state = ALLOC(sizeof(pg_cryptohash_state));
+ if (state == NULL)
{
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
return NULL;
}
+ ctx->data = state;
+ ctx->type = type;
+
+#ifndef FRONTEND
+ ResourceOwnerEnlargeCryptoHash(CurrentResourceOwner);
+#endif
+
+ /*
+ * Initialization takes care of assigning the correct type for OpenSSL.
+ */
+ state->evpctx = EVP_MD_CTX_create();
+
+ if (state->evpctx == NULL)
+ {
+ explicit_bzero(state, sizeof(pg_cryptohash_state));
+ explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
+#else
+ FREE(state);
+ FREE(ctx);
+ return NULL;
+#endif
+ }
+
+#ifndef FRONTEND
+ state->resowner = CurrentResourceOwner;
+ ResourceOwnerRememberCryptoHash(CurrentResourceOwner,
+ PointerGetDatum(ctx));
+#endif
+
return ctx;
}
@@ -85,23 +124,26 @@ int
pg_cryptohash_init(pg_cryptohash_ctx *ctx)
{
int status = 0;
+ pg_cryptohash_state *state;
if (ctx == NULL)
return 0;
+ state = (pg_cryptohash_state *) ctx->data;
+
switch (ctx->type)
{
case PG_SHA224:
- status = SHA224_Init((SHA256_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha224(), NULL);
break;
case PG_SHA256:
- status = SHA256_Init((SHA256_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha256(), NULL);
break;
case PG_SHA384:
- status = SHA384_Init((SHA512_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha384(), NULL);
break;
case PG_SHA512:
- status = SHA512_Init((SHA512_CTX *) ctx->data);
+ status = EVP_DigestInit_ex(state->evpctx, EVP_sha512(), NULL);
break;
}
@@ -120,25 +162,13 @@ int
pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
{
int status = 0;
+ pg_cryptohash_state *state;
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;
- }
+ state = (pg_cryptohash_state *) ctx->data;
+ status = EVP_DigestUpdate(state->evpctx, data, len);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
@@ -155,25 +185,13 @@ int
pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
{
int status = 0;
+ pg_cryptohash_state *state;
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;
- }
+ state = (pg_cryptohash_state *) ctx->data;
+ status = EVP_DigestFinal_ex(state->evpctx, dest, 0);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
@@ -189,9 +207,21 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
void
pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
+ pg_cryptohash_state *state;
+
if (ctx == NULL)
return;
- FREE(ctx->data);
+
+ state = (pg_cryptohash_state *) ctx->data;
+ EVP_MD_CTX_destroy(state->evpctx);
+
+#ifndef FRONTEND
+ ResourceOwnerForgetCryptoHash(state->resowner,
+ PointerGetDatum(ctx));
+#endif
+
+ explicit_bzero(state, sizeof(pg_cryptohash_state));
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+ FREE(state);
FREE(ctx);
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 04464c2e76..cf63acbf6f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3180,6 +3180,7 @@ pg_conv_map
pg_crc32
pg_crc32c
pg_cryptohash_ctx
+pg_cryptohash_state
pg_cryptohash_type
pg_ctype_cache
pg_enc
--
2.29.2
On 3 Dec 2020, at 02:47, Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Dec 02, 2020 at 12:03:49PM +0900, Michael Paquier wrote:
Thanks. 0001 has been applied and the buildfarm does not complain, so
it looks like we are good (I'll take care of any issues, like the one
Fujii-san has just reported). Attached are new patches for 0002, the
EVP switch. One thing I noticed is that we need to free the backup
manifest a bit earlier once we begin to use resource owner in
basebackup.c as there is a specific step that may do a double-free.
This would not happen when not using OpenSSL or on HEAD. It would be
easy to separate the resowner and cryptohash portions of the patch
here, but both are tightly linked, so I'd prefer to keep them
together.Attached is a rebased version to take care of the conflicts introduced
by 91624c2f.
This version looks good to me, and builds/tests without any issues. While I
didn't try to adapt the libnss patch to the resowner machinery, I don't see any
reasons off the cuff why it wouldn't work with the scaffolding provided here.
My only question is:
+#ifndef FRONTEND
+ elog(ERROR, "out of memory");
Shouldn't that be an ereport using ERRCODE_OUT_OF_MEMORY?
cheers ./daniel
On Thu, Dec 03, 2020 at 10:58:39PM +0100, Daniel Gustafsson wrote:
This version looks good to me, and builds/tests without any issues. While I
didn't try to adapt the libnss patch to the resowner machinery, I don't see any
reasons off the cuff why it wouldn't work with the scaffolding provided here.
Based on my read of the code in lib/freebl/, SHA256ContextStr & co
hold the context data for SHA2, but are headers like sha256.h
installed? I don't know enough of NSS to be able to answer to
that. If, like OpenSSL, the context internals are not provided, I
think that you could use SHA256_NewContext() and track the allocation
with the resource owner callbacks, but doing a palloc() would be
much simpler if the context internals are available.
My only question is:
+#ifndef FRONTEND + elog(ERROR, "out of memory"); Shouldn't that be an ereport using ERRCODE_OUT_OF_MEMORY?
That makes sense, fixed.
I have done more testing across all versions of OpenSSL, and applied
this one, meaning that we are done for SHA2. Thanks for the reviews!
Now, moving back to MD5..
--
Michael