postgresql/src/common/hmac_openssl.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

388 lines
7.8 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* hmac_openssl.c
* Implementation of HMAC with OpenSSL.
*
* This should only be used if code is compiled with OpenSSL support.
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/common/hmac_openssl.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <openssl/err.h>
#include <openssl/hmac.h>
#include "common/hmac.h"
#include "common/md5.h"
#include "common/sha1.h"
#include "common/sha2.h"
#ifndef FRONTEND
#include "utils/memutils.h"
#include "utils/resowner.h"
#endif
/*
* In backend, use an allocation in TopMemoryContext to count for resowner
* cleanup handling if necessary. For versions of OpenSSL where HMAC_CTX is
* known, just use palloc(). In frontend, use malloc to be able to return
* a failure status back to the caller.
*/
#ifndef FRONTEND
#ifdef HAVE_HMAC_CTX_NEW
#define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
#else
#define ALLOC(size) palloc(size)
#endif
#define FREE(ptr) pfree(ptr)
#else /* FRONTEND */
#define ALLOC(size) malloc(size)
#define FREE(ptr) free(ptr)
#endif /* FRONTEND */
/* Set of error states */
typedef enum pg_hmac_errno
{
PG_HMAC_ERROR_NONE = 0,
PG_HMAC_ERROR_DEST_LEN,
PG_HMAC_ERROR_OPENSSL,
} pg_hmac_errno;
/* Internal pg_hmac_ctx structure */
struct pg_hmac_ctx
{
HMAC_CTX *hmacctx;
pg_cryptohash_type type;
pg_hmac_errno error;
const char *errreason;
#ifndef FRONTEND
ResourceOwner resowner;
#endif
};
Make ResourceOwners more easily extensible. Instead of having a separate array/hash for each resource kind, use a single array and hash to hold all kinds of resources. This makes it possible to introduce new resource "kinds" without having to modify the ResourceOwnerData struct. In particular, this makes it possible for extensions to register custom resource kinds. The old approach was to have a small array of resources of each kind, and if it fills up, switch to a hash table. The new approach also uses an array and a hash, but now the array and the hash are used at the same time. The array is used to hold the recently added resources, and when it fills up, they are moved to the hash. This keeps the access to recent entries fast, even when there are a lot of long-held resources. All the resource-specific ResourceOwnerEnlarge*(), ResourceOwnerRemember*(), and ResourceOwnerForget*() functions have been replaced with three generic functions that take resource kind as argument. For convenience, we still define resource-specific wrapper macros around the generic functions with the old names, but they are now defined in the source files that use those resource kinds. The release callback no longer needs to call ResourceOwnerForget on the resource being released. ResourceOwnerRelease unregisters the resource from the owner before calling the callback. That needed some changes in bufmgr.c and some other files, where releasing the resources previously always called ResourceOwnerForget. Each resource kind specifies a release priority, and ResourceOwnerReleaseAll releases the resources in priority order. To make that possible, we have to restrict what you can do between phases. After calling ResourceOwnerRelease(), you are no longer allowed to remember any more resources in it or to forget any previously remembered resources by calling ResourceOwnerForget. There was one case where that was done previously. At subtransaction commit, AtEOSubXact_Inval() would handle the invalidation messages and call RelationFlushRelation(), which temporarily increased the reference count on the relation being flushed. We now switch to the parent subtransaction's resource owner before calling AtEOSubXact_Inval(), so that there is a valid ResourceOwner to temporarily hold that relcache reference. Other end-of-xact routines make similar calls to AtEOXact_Inval() between release phases, but I didn't see any regression test failures from those, so I'm not sure if they could reach a codepath that needs remembering extra resources. There were two exceptions to how the resource leak WARNINGs on commit were printed previously: llvmjit silently released the context without printing the warning, and a leaked buffer io triggered a PANIC. Now everything prints a WARNING, including those cases. Add tests in src/test/modules/test_resowner. Reviewed-by: Aleksander Alekseev, Michael Paquier, Julien Rouhaud Reviewed-by: Kyotaro Horiguchi, Hayato Kuroda, Álvaro Herrera, Zhihong Yu Reviewed-by: Peter Eisentraut, Andres Freund Discussion: https://www.postgresql.org/message-id/cbfabeb0-cd3c-e951-a572-19b365ed314d%40iki.fi
2023-11-08 12:30:50 +01:00
/* ResourceOwner callbacks to hold HMAC contexts */
#ifndef FRONTEND
static void ResOwnerReleaseHMAC(Datum res);
static const ResourceOwnerDesc hmac_resowner_desc =
{
.name = "OpenSSL HMAC context",
.release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
.release_priority = RELEASE_PRIO_HMAC_CONTEXTS,
.ReleaseResource = ResOwnerReleaseHMAC,
.DebugPrint = NULL /* the default message is fine */
};
/* Convenience wrappers over ResourceOwnerRemember/Forget */
static inline void
ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
{
ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
}
static inline void
ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
{
ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
}
#endif
static const char *
SSLerrmessage(unsigned long ecode)
{
if (ecode == 0)
return NULL;
/*
* This may return NULL, but we would fall back to a default error path if
* that were the case.
*/
return ERR_reason_error_string(ecode);
}
/*
* pg_hmac_create
*
* Allocate a hash context. Returns NULL on failure for an OOM. The
* backend issues an error, without returning.
*/
pg_hmac_ctx *
pg_hmac_create(pg_cryptohash_type type)
{
pg_hmac_ctx *ctx;
ctx = ALLOC(sizeof(pg_hmac_ctx));
if (ctx == NULL)
return NULL;
memset(ctx, 0, sizeof(pg_hmac_ctx));
ctx->type = type;
ctx->error = PG_HMAC_ERROR_NONE;
ctx->errreason = NULL;
/*
* Initialization takes care of assigning the correct type for OpenSSL.
* Also ensure that there aren't any unconsumed errors in the queue from
* previous runs.
*/
ERR_clear_error();
#ifdef HAVE_HMAC_CTX_NEW
#ifndef FRONTEND
Make ResourceOwners more easily extensible. Instead of having a separate array/hash for each resource kind, use a single array and hash to hold all kinds of resources. This makes it possible to introduce new resource "kinds" without having to modify the ResourceOwnerData struct. In particular, this makes it possible for extensions to register custom resource kinds. The old approach was to have a small array of resources of each kind, and if it fills up, switch to a hash table. The new approach also uses an array and a hash, but now the array and the hash are used at the same time. The array is used to hold the recently added resources, and when it fills up, they are moved to the hash. This keeps the access to recent entries fast, even when there are a lot of long-held resources. All the resource-specific ResourceOwnerEnlarge*(), ResourceOwnerRemember*(), and ResourceOwnerForget*() functions have been replaced with three generic functions that take resource kind as argument. For convenience, we still define resource-specific wrapper macros around the generic functions with the old names, but they are now defined in the source files that use those resource kinds. The release callback no longer needs to call ResourceOwnerForget on the resource being released. ResourceOwnerRelease unregisters the resource from the owner before calling the callback. That needed some changes in bufmgr.c and some other files, where releasing the resources previously always called ResourceOwnerForget. Each resource kind specifies a release priority, and ResourceOwnerReleaseAll releases the resources in priority order. To make that possible, we have to restrict what you can do between phases. After calling ResourceOwnerRelease(), you are no longer allowed to remember any more resources in it or to forget any previously remembered resources by calling ResourceOwnerForget. There was one case where that was done previously. At subtransaction commit, AtEOSubXact_Inval() would handle the invalidation messages and call RelationFlushRelation(), which temporarily increased the reference count on the relation being flushed. We now switch to the parent subtransaction's resource owner before calling AtEOSubXact_Inval(), so that there is a valid ResourceOwner to temporarily hold that relcache reference. Other end-of-xact routines make similar calls to AtEOXact_Inval() between release phases, but I didn't see any regression test failures from those, so I'm not sure if they could reach a codepath that needs remembering extra resources. There were two exceptions to how the resource leak WARNINGs on commit were printed previously: llvmjit silently released the context without printing the warning, and a leaked buffer io triggered a PANIC. Now everything prints a WARNING, including those cases. Add tests in src/test/modules/test_resowner. Reviewed-by: Aleksander Alekseev, Michael Paquier, Julien Rouhaud Reviewed-by: Kyotaro Horiguchi, Hayato Kuroda, Álvaro Herrera, Zhihong Yu Reviewed-by: Peter Eisentraut, Andres Freund Discussion: https://www.postgresql.org/message-id/cbfabeb0-cd3c-e951-a572-19b365ed314d%40iki.fi
2023-11-08 12:30:50 +01:00
ResourceOwnerEnlarge(CurrentResourceOwner);
#endif
ctx->hmacctx = HMAC_CTX_new();
#else
ctx->hmacctx = ALLOC(sizeof(HMAC_CTX));
#endif
if (ctx->hmacctx == NULL)
{
explicit_bzero(ctx, sizeof(pg_hmac_ctx));
FREE(ctx);
#ifndef FRONTEND
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
#endif
return NULL;
}
#ifdef HAVE_HMAC_CTX_NEW
#ifndef FRONTEND
ctx->resowner = CurrentResourceOwner;
Make ResourceOwners more easily extensible. Instead of having a separate array/hash for each resource kind, use a single array and hash to hold all kinds of resources. This makes it possible to introduce new resource "kinds" without having to modify the ResourceOwnerData struct. In particular, this makes it possible for extensions to register custom resource kinds. The old approach was to have a small array of resources of each kind, and if it fills up, switch to a hash table. The new approach also uses an array and a hash, but now the array and the hash are used at the same time. The array is used to hold the recently added resources, and when it fills up, they are moved to the hash. This keeps the access to recent entries fast, even when there are a lot of long-held resources. All the resource-specific ResourceOwnerEnlarge*(), ResourceOwnerRemember*(), and ResourceOwnerForget*() functions have been replaced with three generic functions that take resource kind as argument. For convenience, we still define resource-specific wrapper macros around the generic functions with the old names, but they are now defined in the source files that use those resource kinds. The release callback no longer needs to call ResourceOwnerForget on the resource being released. ResourceOwnerRelease unregisters the resource from the owner before calling the callback. That needed some changes in bufmgr.c and some other files, where releasing the resources previously always called ResourceOwnerForget. Each resource kind specifies a release priority, and ResourceOwnerReleaseAll releases the resources in priority order. To make that possible, we have to restrict what you can do between phases. After calling ResourceOwnerRelease(), you are no longer allowed to remember any more resources in it or to forget any previously remembered resources by calling ResourceOwnerForget. There was one case where that was done previously. At subtransaction commit, AtEOSubXact_Inval() would handle the invalidation messages and call RelationFlushRelation(), which temporarily increased the reference count on the relation being flushed. We now switch to the parent subtransaction's resource owner before calling AtEOSubXact_Inval(), so that there is a valid ResourceOwner to temporarily hold that relcache reference. Other end-of-xact routines make similar calls to AtEOXact_Inval() between release phases, but I didn't see any regression test failures from those, so I'm not sure if they could reach a codepath that needs remembering extra resources. There were two exceptions to how the resource leak WARNINGs on commit were printed previously: llvmjit silently released the context without printing the warning, and a leaked buffer io triggered a PANIC. Now everything prints a WARNING, including those cases. Add tests in src/test/modules/test_resowner. Reviewed-by: Aleksander Alekseev, Michael Paquier, Julien Rouhaud Reviewed-by: Kyotaro Horiguchi, Hayato Kuroda, Álvaro Herrera, Zhihong Yu Reviewed-by: Peter Eisentraut, Andres Freund Discussion: https://www.postgresql.org/message-id/cbfabeb0-cd3c-e951-a572-19b365ed314d%40iki.fi
2023-11-08 12:30:50 +01:00
ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
#endif
#else
memset(ctx->hmacctx, 0, sizeof(HMAC_CTX));
#endif /* HAVE_HMAC_CTX_NEW */
return ctx;
}
/*
* pg_hmac_init
*
* Initialize a HMAC context. Returns 0 on success, -1 on failure.
*/
int
pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
{
int status = 0;
if (ctx == NULL)
return -1;
switch (ctx->type)
{
case PG_MD5:
status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
break;
case PG_SHA1:
status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
break;
case PG_SHA224:
status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
break;
case PG_SHA256:
status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
break;
case PG_SHA384:
status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
break;
case PG_SHA512:
status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
break;
}
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
{
ctx->errreason = SSLerrmessage(ERR_get_error());
ctx->error = PG_HMAC_ERROR_OPENSSL;
return -1;
}
return 0;
}
/*
* pg_hmac_update
*
* Update a HMAC context. Returns 0 on success, -1 on failure.
*/
int
pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
{
int status = 0;
if (ctx == NULL)
return -1;
status = HMAC_Update(ctx->hmacctx, data, len);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
{
ctx->errreason = SSLerrmessage(ERR_get_error());
ctx->error = PG_HMAC_ERROR_OPENSSL;
return -1;
}
return 0;
}
/*
* pg_hmac_final
*
* Finalize a HMAC context. Returns 0 on success, -1 on failure.
*/
int
pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
{
int status = 0;
uint32 outlen;
if (ctx == NULL)
return -1;
switch (ctx->type)
{
case PG_MD5:
if (len < MD5_DIGEST_LENGTH)
{
ctx->error = PG_HMAC_ERROR_DEST_LEN;
return -1;
}
break;
case PG_SHA1:
if (len < SHA1_DIGEST_LENGTH)
{
ctx->error = PG_HMAC_ERROR_DEST_LEN;
return -1;
}
break;
case PG_SHA224:
if (len < PG_SHA224_DIGEST_LENGTH)
{
ctx->error = PG_HMAC_ERROR_DEST_LEN;
return -1;
}
break;
case PG_SHA256:
if (len < PG_SHA256_DIGEST_LENGTH)
{
ctx->error = PG_HMAC_ERROR_DEST_LEN;
return -1;
}
break;
case PG_SHA384:
if (len < PG_SHA384_DIGEST_LENGTH)
{
ctx->error = PG_HMAC_ERROR_DEST_LEN;
return -1;
}
break;
case PG_SHA512:
if (len < PG_SHA512_DIGEST_LENGTH)
{
ctx->error = PG_HMAC_ERROR_DEST_LEN;
return -1;
}
break;
}
status = HMAC_Final(ctx->hmacctx, dest, &outlen);
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
{
ctx->errreason = SSLerrmessage(ERR_get_error());
ctx->error = PG_HMAC_ERROR_OPENSSL;
return -1;
}
return 0;
}
/*
* pg_hmac_free
*
* Free a HMAC context.
*/
void
pg_hmac_free(pg_hmac_ctx *ctx)
{
if (ctx == NULL)
return;
#ifdef HAVE_HMAC_CTX_FREE
HMAC_CTX_free(ctx->hmacctx);
#ifndef FRONTEND
Make ResourceOwners more easily extensible. Instead of having a separate array/hash for each resource kind, use a single array and hash to hold all kinds of resources. This makes it possible to introduce new resource "kinds" without having to modify the ResourceOwnerData struct. In particular, this makes it possible for extensions to register custom resource kinds. The old approach was to have a small array of resources of each kind, and if it fills up, switch to a hash table. The new approach also uses an array and a hash, but now the array and the hash are used at the same time. The array is used to hold the recently added resources, and when it fills up, they are moved to the hash. This keeps the access to recent entries fast, even when there are a lot of long-held resources. All the resource-specific ResourceOwnerEnlarge*(), ResourceOwnerRemember*(), and ResourceOwnerForget*() functions have been replaced with three generic functions that take resource kind as argument. For convenience, we still define resource-specific wrapper macros around the generic functions with the old names, but they are now defined in the source files that use those resource kinds. The release callback no longer needs to call ResourceOwnerForget on the resource being released. ResourceOwnerRelease unregisters the resource from the owner before calling the callback. That needed some changes in bufmgr.c and some other files, where releasing the resources previously always called ResourceOwnerForget. Each resource kind specifies a release priority, and ResourceOwnerReleaseAll releases the resources in priority order. To make that possible, we have to restrict what you can do between phases. After calling ResourceOwnerRelease(), you are no longer allowed to remember any more resources in it or to forget any previously remembered resources by calling ResourceOwnerForget. There was one case where that was done previously. At subtransaction commit, AtEOSubXact_Inval() would handle the invalidation messages and call RelationFlushRelation(), which temporarily increased the reference count on the relation being flushed. We now switch to the parent subtransaction's resource owner before calling AtEOSubXact_Inval(), so that there is a valid ResourceOwner to temporarily hold that relcache reference. Other end-of-xact routines make similar calls to AtEOXact_Inval() between release phases, but I didn't see any regression test failures from those, so I'm not sure if they could reach a codepath that needs remembering extra resources. There were two exceptions to how the resource leak WARNINGs on commit were printed previously: llvmjit silently released the context without printing the warning, and a leaked buffer io triggered a PANIC. Now everything prints a WARNING, including those cases. Add tests in src/test/modules/test_resowner. Reviewed-by: Aleksander Alekseev, Michael Paquier, Julien Rouhaud Reviewed-by: Kyotaro Horiguchi, Hayato Kuroda, Álvaro Herrera, Zhihong Yu Reviewed-by: Peter Eisentraut, Andres Freund Discussion: https://www.postgresql.org/message-id/cbfabeb0-cd3c-e951-a572-19b365ed314d%40iki.fi
2023-11-08 12:30:50 +01:00
if (ctx->resowner)
ResourceOwnerForgetHMAC(ctx->resowner, ctx);
#endif
#else
explicit_bzero(ctx->hmacctx, sizeof(HMAC_CTX));
FREE(ctx->hmacctx);
#endif
explicit_bzero(ctx, sizeof(pg_hmac_ctx));
FREE(ctx);
}
/*
* pg_hmac_error
*
* Returns a static string providing details about an error that happened
* during a HMAC computation.
*/
const char *
pg_hmac_error(pg_hmac_ctx *ctx)
{
if (ctx == NULL)
return _("out of memory");
/*
* If a reason is provided, rely on it, else fallback to any error code
* set.
*/
if (ctx->errreason)
return ctx->errreason;
switch (ctx->error)
{
case PG_HMAC_ERROR_NONE:
return _("success");
case PG_HMAC_ERROR_DEST_LEN:
return _("destination buffer too small");
case PG_HMAC_ERROR_OPENSSL:
return _("OpenSSL failure");
}
Assert(false); /* cannot be reached */
return _("success");
}
Make ResourceOwners more easily extensible. Instead of having a separate array/hash for each resource kind, use a single array and hash to hold all kinds of resources. This makes it possible to introduce new resource "kinds" without having to modify the ResourceOwnerData struct. In particular, this makes it possible for extensions to register custom resource kinds. The old approach was to have a small array of resources of each kind, and if it fills up, switch to a hash table. The new approach also uses an array and a hash, but now the array and the hash are used at the same time. The array is used to hold the recently added resources, and when it fills up, they are moved to the hash. This keeps the access to recent entries fast, even when there are a lot of long-held resources. All the resource-specific ResourceOwnerEnlarge*(), ResourceOwnerRemember*(), and ResourceOwnerForget*() functions have been replaced with three generic functions that take resource kind as argument. For convenience, we still define resource-specific wrapper macros around the generic functions with the old names, but they are now defined in the source files that use those resource kinds. The release callback no longer needs to call ResourceOwnerForget on the resource being released. ResourceOwnerRelease unregisters the resource from the owner before calling the callback. That needed some changes in bufmgr.c and some other files, where releasing the resources previously always called ResourceOwnerForget. Each resource kind specifies a release priority, and ResourceOwnerReleaseAll releases the resources in priority order. To make that possible, we have to restrict what you can do between phases. After calling ResourceOwnerRelease(), you are no longer allowed to remember any more resources in it or to forget any previously remembered resources by calling ResourceOwnerForget. There was one case where that was done previously. At subtransaction commit, AtEOSubXact_Inval() would handle the invalidation messages and call RelationFlushRelation(), which temporarily increased the reference count on the relation being flushed. We now switch to the parent subtransaction's resource owner before calling AtEOSubXact_Inval(), so that there is a valid ResourceOwner to temporarily hold that relcache reference. Other end-of-xact routines make similar calls to AtEOXact_Inval() between release phases, but I didn't see any regression test failures from those, so I'm not sure if they could reach a codepath that needs remembering extra resources. There were two exceptions to how the resource leak WARNINGs on commit were printed previously: llvmjit silently released the context without printing the warning, and a leaked buffer io triggered a PANIC. Now everything prints a WARNING, including those cases. Add tests in src/test/modules/test_resowner. Reviewed-by: Aleksander Alekseev, Michael Paquier, Julien Rouhaud Reviewed-by: Kyotaro Horiguchi, Hayato Kuroda, Álvaro Herrera, Zhihong Yu Reviewed-by: Peter Eisentraut, Andres Freund Discussion: https://www.postgresql.org/message-id/cbfabeb0-cd3c-e951-a572-19b365ed314d%40iki.fi
2023-11-08 12:30:50 +01:00
/* ResourceOwner callbacks */
#ifndef FRONTEND
static void
ResOwnerReleaseHMAC(Datum res)
{
pg_hmac_ctx *ctx = (pg_hmac_ctx *) DatumGetPointer(res);
ctx->resowner = NULL;
pg_hmac_free(ctx);
}
#endif