LCOV - code coverage report
Current view: top level - src/common - cryptohash_openssl.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 60.7 % 135 82
Test Date: 2026-03-03 23:14:44 Functions: 80.0 % 10 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * cryptohash_openssl.c
       4              :  *    Set of wrapper routines on top of OpenSSL to support cryptographic
       5              :  *    hash functions.
       6              :  *
       7              :  * This should only be used if code is compiled with OpenSSL support.
       8              :  *
       9              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      10              :  * Portions Copyright (c) 1994, Regents of the University of California
      11              :  *
      12              :  * IDENTIFICATION
      13              :  *        src/common/cryptohash_openssl.c
      14              :  *
      15              :  *-------------------------------------------------------------------------
      16              :  */
      17              : 
      18              : #ifndef FRONTEND
      19              : #include "postgres.h"
      20              : #else
      21              : #include "postgres_fe.h"
      22              : #endif
      23              : 
      24              : #include <openssl/err.h>
      25              : #include <openssl/evp.h>
      26              : 
      27              : #include "common/cryptohash.h"
      28              : #include "common/md5.h"
      29              : #include "common/sha1.h"
      30              : #include "common/sha2.h"
      31              : #ifndef FRONTEND
      32              : #include "utils/memutils.h"
      33              : #include "utils/resowner.h"
      34              : #endif
      35              : 
      36              : /*
      37              :  * In the backend, use an allocation in TopMemoryContext to count for
      38              :  * resowner cleanup handling.  In the frontend, use malloc to be able
      39              :  * to return a failure status back to the caller.
      40              :  */
      41              : #ifndef FRONTEND
      42              : #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
      43              : #define FREE(ptr) pfree(ptr)
      44              : #else
      45              : #define ALLOC(size) malloc(size)
      46              : #define FREE(ptr) free(ptr)
      47              : #endif
      48              : 
      49              : /* Set of error states */
      50              : typedef enum pg_cryptohash_errno
      51              : {
      52              :     PG_CRYPTOHASH_ERROR_NONE = 0,
      53              :     PG_CRYPTOHASH_ERROR_DEST_LEN,
      54              :     PG_CRYPTOHASH_ERROR_OPENSSL,
      55              : } pg_cryptohash_errno;
      56              : 
      57              : /*
      58              :  * Internal pg_cryptohash_ctx structure.
      59              :  *
      60              :  * This tracks the resource owner associated to each EVP context data
      61              :  * for the backend.
      62              :  */
      63              : struct pg_cryptohash_ctx
      64              : {
      65              :     pg_cryptohash_type type;
      66              :     pg_cryptohash_errno error;
      67              :     const char *errreason;
      68              : 
      69              :     EVP_MD_CTX *evpctx;
      70              : 
      71              : #ifndef FRONTEND
      72              :     ResourceOwner resowner;
      73              : #endif
      74              : };
      75              : 
      76              : /* ResourceOwner callbacks to hold cryptohash contexts */
      77              : #ifndef FRONTEND
      78              : static void ResOwnerReleaseCryptoHash(Datum res);
      79              : 
      80              : static const ResourceOwnerDesc cryptohash_resowner_desc =
      81              : {
      82              :     .name = "OpenSSL cryptohash context",
      83              :     .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
      84              :     .release_priority = RELEASE_PRIO_CRYPTOHASH_CONTEXTS,
      85              :     .ReleaseResource = ResOwnerReleaseCryptoHash,
      86              :     .DebugPrint = NULL          /* the default message is fine */
      87              : };
      88              : 
      89              : /* Convenience wrappers over ResourceOwnerRemember/Forget */
      90              : static inline void
      91       650753 : ResourceOwnerRememberCryptoHash(ResourceOwner owner, pg_cryptohash_ctx *ctx)
      92              : {
      93       650753 :     ResourceOwnerRemember(owner, PointerGetDatum(ctx), &cryptohash_resowner_desc);
      94       650753 : }
      95              : static inline void
      96       650743 : ResourceOwnerForgetCryptoHash(ResourceOwner owner, pg_cryptohash_ctx *ctx)
      97              : {
      98       650743 :     ResourceOwnerForget(owner, PointerGetDatum(ctx), &cryptohash_resowner_desc);
      99       650743 : }
     100              : #endif
     101              : 
     102              : static const char *
     103            0 : SSLerrmessage(unsigned long ecode)
     104              : {
     105            0 :     if (ecode == 0)
     106            0 :         return NULL;
     107              : 
     108              :     /*
     109              :      * This may return NULL, but we would fall back to a default error path if
     110              :      * that were the case.
     111              :      */
     112            0 :     return ERR_reason_error_string(ecode);
     113              : }
     114              : 
     115              : /*
     116              :  * pg_cryptohash_create
     117              :  *
     118              :  * Allocate a hash context.  Returns NULL on failure for an OOM.  The
     119              :  * backend issues an error, without returning.
     120              :  */
     121              : pg_cryptohash_ctx *
     122       660620 : pg_cryptohash_create(pg_cryptohash_type type)
     123              : {
     124              :     pg_cryptohash_ctx *ctx;
     125              : 
     126              :     /*
     127              :      * Make sure that the resource owner has space to remember this reference.
     128              :      * This can error out with "out of memory", so do this before any other
     129              :      * allocation to avoid leaking.
     130              :      */
     131              : #ifndef FRONTEND
     132       650753 :     ResourceOwnerEnlarge(CurrentResourceOwner);
     133              : #endif
     134              : 
     135       660620 :     ctx = ALLOC(sizeof(pg_cryptohash_ctx));
     136       660620 :     if (ctx == NULL)
     137            0 :         return NULL;
     138       660620 :     memset(ctx, 0, sizeof(pg_cryptohash_ctx));
     139       660620 :     ctx->type = type;
     140       660620 :     ctx->error = PG_CRYPTOHASH_ERROR_NONE;
     141       660620 :     ctx->errreason = NULL;
     142              : 
     143              :     /*
     144              :      * Initialization takes care of assigning the correct type for OpenSSL.
     145              :      * Also ensure that there aren't any unconsumed errors in the queue from
     146              :      * previous runs.
     147              :      */
     148       660620 :     ERR_clear_error();
     149       660620 :     ctx->evpctx = EVP_MD_CTX_create();
     150              : 
     151       660620 :     if (ctx->evpctx == NULL)
     152              :     {
     153            0 :         explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
     154            0 :         FREE(ctx);
     155              : #ifndef FRONTEND
     156            0 :         ereport(ERROR,
     157              :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     158              :                  errmsg("out of memory")));
     159              : #else
     160            0 :         return NULL;
     161              : #endif
     162              :     }
     163              : 
     164              : #ifndef FRONTEND
     165       650753 :     ctx->resowner = CurrentResourceOwner;
     166       650753 :     ResourceOwnerRememberCryptoHash(CurrentResourceOwner, ctx);
     167              : #endif
     168              : 
     169       660620 :     return ctx;
     170              : }
     171              : 
     172              : /*
     173              :  * pg_cryptohash_init
     174              :  *
     175              :  * Initialize a hash context.  Returns 0 on success, and -1 on failure.
     176              :  */
     177              : int
     178       660620 : pg_cryptohash_init(pg_cryptohash_ctx *ctx)
     179              : {
     180       660620 :     int         status = 0;
     181              : 
     182       660620 :     if (ctx == NULL)
     183            0 :         return -1;
     184              : 
     185       660620 :     switch (ctx->type)
     186              :     {
     187           71 :         case PG_MD5:
     188           71 :             status = EVP_DigestInit_ex(ctx->evpctx, EVP_md5(), NULL);
     189           71 :             break;
     190            0 :         case PG_SHA1:
     191            0 :             status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha1(), NULL);
     192            0 :             break;
     193         5813 :         case PG_SHA224:
     194         5813 :             status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha224(), NULL);
     195         5813 :             break;
     196       646980 :         case PG_SHA256:
     197       646980 :             status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha256(), NULL);
     198       646980 :             break;
     199         3878 :         case PG_SHA384:
     200         3878 :             status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha384(), NULL);
     201         3878 :             break;
     202         3878 :         case PG_SHA512:
     203         3878 :             status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha512(), NULL);
     204         3878 :             break;
     205              :     }
     206              : 
     207              :     /* OpenSSL internals return 1 on success, 0 on failure */
     208       660620 :     if (status <= 0)
     209              :     {
     210            0 :         ctx->errreason = SSLerrmessage(ERR_get_error());
     211            0 :         ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
     212              : 
     213              :         /*
     214              :          * The OpenSSL error queue should normally be empty since we've
     215              :          * consumed an error, but cipher initialization can in FIPS-enabled
     216              :          * OpenSSL builds generate two errors so clear the queue here as well.
     217              :          */
     218            0 :         ERR_clear_error();
     219            0 :         return -1;
     220              :     }
     221       660620 :     return 0;
     222              : }
     223              : 
     224              : /*
     225              :  * pg_cryptohash_update
     226              :  *
     227              :  * Update a hash context.  Returns 0 on success, and -1 on failure.
     228              :  */
     229              : int
     230       828049 : pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
     231              : {
     232       828049 :     int         status = 0;
     233              : 
     234       828049 :     if (ctx == NULL)
     235            0 :         return -1;
     236              : 
     237       828049 :     status = EVP_DigestUpdate(ctx->evpctx, data, len);
     238              : 
     239              :     /* OpenSSL internals return 1 on success, 0 on failure */
     240       828049 :     if (status <= 0)
     241              :     {
     242            0 :         ctx->errreason = SSLerrmessage(ERR_get_error());
     243            0 :         ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
     244            0 :         return -1;
     245              :     }
     246       828049 :     return 0;
     247              : }
     248              : 
     249              : /*
     250              :  * pg_cryptohash_final
     251              :  *
     252              :  * Finalize a hash context.  Returns 0 on success, and -1 on failure.
     253              :  */
     254              : int
     255       660612 : pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
     256              : {
     257       660612 :     int         status = 0;
     258              : 
     259       660612 :     if (ctx == NULL)
     260            0 :         return -1;
     261              : 
     262       660612 :     switch (ctx->type)
     263              :     {
     264           71 :         case PG_MD5:
     265           71 :             if (len < MD5_DIGEST_LENGTH)
     266              :             {
     267            0 :                 ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
     268            0 :                 return -1;
     269              :             }
     270           71 :             break;
     271            0 :         case PG_SHA1:
     272            0 :             if (len < SHA1_DIGEST_LENGTH)
     273              :             {
     274            0 :                 ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
     275            0 :                 return -1;
     276              :             }
     277            0 :             break;
     278         5813 :         case PG_SHA224:
     279         5813 :             if (len < PG_SHA224_DIGEST_LENGTH)
     280              :             {
     281            0 :                 ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
     282            0 :                 return -1;
     283              :             }
     284         5813 :             break;
     285       646972 :         case PG_SHA256:
     286       646972 :             if (len < PG_SHA256_DIGEST_LENGTH)
     287              :             {
     288            0 :                 ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
     289            0 :                 return -1;
     290              :             }
     291       646972 :             break;
     292         3878 :         case PG_SHA384:
     293         3878 :             if (len < PG_SHA384_DIGEST_LENGTH)
     294              :             {
     295            0 :                 ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
     296            0 :                 return -1;
     297              :             }
     298         3878 :             break;
     299         3878 :         case PG_SHA512:
     300         3878 :             if (len < PG_SHA512_DIGEST_LENGTH)
     301              :             {
     302            0 :                 ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
     303            0 :                 return -1;
     304              :             }
     305         3878 :             break;
     306              :     }
     307              : 
     308       660612 :     status = EVP_DigestFinal_ex(ctx->evpctx, dest, 0);
     309              : 
     310              :     /* OpenSSL internals return 1 on success, 0 on failure */
     311       660612 :     if (status <= 0)
     312              :     {
     313            0 :         ctx->errreason = SSLerrmessage(ERR_get_error());
     314            0 :         ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
     315            0 :         return -1;
     316              :     }
     317       660612 :     return 0;
     318              : }
     319              : 
     320              : /*
     321              :  * pg_cryptohash_free
     322              :  *
     323              :  * Free a hash context.
     324              :  */
     325              : void
     326       660617 : pg_cryptohash_free(pg_cryptohash_ctx *ctx)
     327              : {
     328       660617 :     if (ctx == NULL)
     329            1 :         return;
     330              : 
     331       660616 :     EVP_MD_CTX_destroy(ctx->evpctx);
     332              : 
     333              : #ifndef FRONTEND
     334       650753 :     if (ctx->resowner)
     335       650743 :         ResourceOwnerForgetCryptoHash(ctx->resowner, ctx);
     336              : #endif
     337              : 
     338       660616 :     explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
     339       660616 :     FREE(ctx);
     340              : }
     341              : 
     342              : /*
     343              :  * pg_cryptohash_error
     344              :  *
     345              :  * Returns a static string providing details about an error that
     346              :  * happened during a computation.
     347              :  */
     348              : const char *
     349            0 : pg_cryptohash_error(pg_cryptohash_ctx *ctx)
     350              : {
     351              :     /*
     352              :      * This implementation would never fail because of an out-of-memory error,
     353              :      * except when creating the context.
     354              :      */
     355            0 :     if (ctx == NULL)
     356            0 :         return _("out of memory");
     357              : 
     358              :     /*
     359              :      * If a reason is provided, rely on it, else fallback to any error code
     360              :      * set.
     361              :      */
     362            0 :     if (ctx->errreason)
     363            0 :         return ctx->errreason;
     364              : 
     365            0 :     switch (ctx->error)
     366              :     {
     367            0 :         case PG_CRYPTOHASH_ERROR_NONE:
     368            0 :             return _("success");
     369            0 :         case PG_CRYPTOHASH_ERROR_DEST_LEN:
     370            0 :             return _("destination buffer too small");
     371            0 :         case PG_CRYPTOHASH_ERROR_OPENSSL:
     372            0 :             return _("OpenSSL failure");
     373              :     }
     374              : 
     375              :     Assert(false);              /* cannot be reached */
     376            0 :     return _("success");
     377              : }
     378              : 
     379              : /* ResourceOwner callbacks */
     380              : 
     381              : #ifndef FRONTEND
     382              : static void
     383           10 : ResOwnerReleaseCryptoHash(Datum res)
     384              : {
     385           10 :     pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) DatumGetPointer(res);
     386              : 
     387           10 :     ctx->resowner = NULL;
     388           10 :     pg_cryptohash_free(ctx);
     389           10 : }
     390              : #endif
        

Generated by: LCOV version 2.0-1