jsonpath scanner: reentrant scanner
[pgsql.git] / src / common / hmac_openssl.c
blobda2c93e32ac71586153a3318ba7d603c23e1e719
1 /*-------------------------------------------------------------------------
3 * hmac_openssl.c
4 * Implementation of HMAC with OpenSSL.
6 * This should only be used if code is compiled with OpenSSL support.
8 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
11 * IDENTIFICATION
12 * src/common/hmac_openssl.c
14 *-------------------------------------------------------------------------
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
24 #include <openssl/err.h>
25 #include <openssl/hmac.h>
27 #include "common/hmac.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
37 * In backend, use an allocation in TopMemoryContext to count for resowner
38 * cleanup handling if necessary. In frontend, use malloc to be able to return
39 * a failure status back to the caller.
41 #ifndef FRONTEND
42 #define USE_RESOWNER_FOR_HMAC
43 #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
44 #define FREE(ptr) pfree(ptr)
45 #else /* FRONTEND */
46 #define ALLOC(size) malloc(size)
47 #define FREE(ptr) free(ptr)
48 #endif /* FRONTEND */
50 /* Set of error states */
51 typedef enum pg_hmac_errno
53 PG_HMAC_ERROR_NONE = 0,
54 PG_HMAC_ERROR_DEST_LEN,
55 PG_HMAC_ERROR_OPENSSL,
56 } pg_hmac_errno;
58 /* Internal pg_hmac_ctx structure */
59 struct pg_hmac_ctx
61 HMAC_CTX *hmacctx;
62 pg_cryptohash_type type;
63 pg_hmac_errno error;
64 const char *errreason;
66 #ifdef USE_RESOWNER_FOR_HMAC
67 ResourceOwner resowner;
68 #endif
71 /* ResourceOwner callbacks to hold HMAC contexts */
72 #ifdef USE_RESOWNER_FOR_HMAC
73 static void ResOwnerReleaseHMAC(Datum res);
75 static const ResourceOwnerDesc hmac_resowner_desc =
77 .name = "OpenSSL HMAC context",
78 .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
79 .release_priority = RELEASE_PRIO_HMAC_CONTEXTS,
80 .ReleaseResource = ResOwnerReleaseHMAC,
81 .DebugPrint = NULL /* the default message is fine */
84 /* Convenience wrappers over ResourceOwnerRemember/Forget */
85 static inline void
86 ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
88 ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
90 static inline void
91 ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
93 ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
95 #endif
97 static const char *
98 SSLerrmessage(unsigned long ecode)
100 if (ecode == 0)
101 return NULL;
104 * This may return NULL, but we would fall back to a default error path if
105 * that were the case.
107 return ERR_reason_error_string(ecode);
111 * pg_hmac_create
113 * Allocate a hash context. Returns NULL on failure for an OOM. The
114 * backend issues an error, without returning.
116 pg_hmac_ctx *
117 pg_hmac_create(pg_cryptohash_type type)
119 pg_hmac_ctx *ctx;
121 ctx = ALLOC(sizeof(pg_hmac_ctx));
122 if (ctx == NULL)
123 return NULL;
124 memset(ctx, 0, sizeof(pg_hmac_ctx));
126 ctx->type = type;
127 ctx->error = PG_HMAC_ERROR_NONE;
128 ctx->errreason = NULL;
132 * Initialization takes care of assigning the correct type for OpenSSL.
133 * Also ensure that there aren't any unconsumed errors in the queue from
134 * previous runs.
136 ERR_clear_error();
138 #ifdef USE_RESOWNER_FOR_HMAC
139 ResourceOwnerEnlarge(CurrentResourceOwner);
140 #endif
142 ctx->hmacctx = HMAC_CTX_new();
144 if (ctx->hmacctx == NULL)
146 explicit_bzero(ctx, sizeof(pg_hmac_ctx));
147 FREE(ctx);
148 #ifndef FRONTEND
149 ereport(ERROR,
150 (errcode(ERRCODE_OUT_OF_MEMORY),
151 errmsg("out of memory")));
152 #endif
153 return NULL;
157 #ifdef USE_RESOWNER_FOR_HMAC
158 ctx->resowner = CurrentResourceOwner;
159 ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
160 #endif
162 return ctx;
166 * pg_hmac_init
168 * Initialize a HMAC context. Returns 0 on success, -1 on failure.
171 pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
173 int status = 0;
175 if (ctx == NULL)
176 return -1;
178 switch (ctx->type)
180 case PG_MD5:
181 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
182 break;
183 case PG_SHA1:
184 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
185 break;
186 case PG_SHA224:
187 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
188 break;
189 case PG_SHA256:
190 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
191 break;
192 case PG_SHA384:
193 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
194 break;
195 case PG_SHA512:
196 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
197 break;
200 /* OpenSSL internals return 1 on success, 0 on failure */
201 if (status <= 0)
203 ctx->errreason = SSLerrmessage(ERR_get_error());
204 ctx->error = PG_HMAC_ERROR_OPENSSL;
205 return -1;
208 return 0;
212 * pg_hmac_update
214 * Update a HMAC context. Returns 0 on success, -1 on failure.
217 pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
219 int status = 0;
221 if (ctx == NULL)
222 return -1;
224 status = HMAC_Update(ctx->hmacctx, data, len);
226 /* OpenSSL internals return 1 on success, 0 on failure */
227 if (status <= 0)
229 ctx->errreason = SSLerrmessage(ERR_get_error());
230 ctx->error = PG_HMAC_ERROR_OPENSSL;
231 return -1;
233 return 0;
237 * pg_hmac_final
239 * Finalize a HMAC context. Returns 0 on success, -1 on failure.
242 pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
244 int status = 0;
245 uint32 outlen;
247 if (ctx == NULL)
248 return -1;
250 switch (ctx->type)
252 case PG_MD5:
253 if (len < MD5_DIGEST_LENGTH)
255 ctx->error = PG_HMAC_ERROR_DEST_LEN;
256 return -1;
258 break;
259 case PG_SHA1:
260 if (len < SHA1_DIGEST_LENGTH)
262 ctx->error = PG_HMAC_ERROR_DEST_LEN;
263 return -1;
265 break;
266 case PG_SHA224:
267 if (len < PG_SHA224_DIGEST_LENGTH)
269 ctx->error = PG_HMAC_ERROR_DEST_LEN;
270 return -1;
272 break;
273 case PG_SHA256:
274 if (len < PG_SHA256_DIGEST_LENGTH)
276 ctx->error = PG_HMAC_ERROR_DEST_LEN;
277 return -1;
279 break;
280 case PG_SHA384:
281 if (len < PG_SHA384_DIGEST_LENGTH)
283 ctx->error = PG_HMAC_ERROR_DEST_LEN;
284 return -1;
286 break;
287 case PG_SHA512:
288 if (len < PG_SHA512_DIGEST_LENGTH)
290 ctx->error = PG_HMAC_ERROR_DEST_LEN;
291 return -1;
293 break;
296 status = HMAC_Final(ctx->hmacctx, dest, &outlen);
298 /* OpenSSL internals return 1 on success, 0 on failure */
299 if (status <= 0)
301 ctx->errreason = SSLerrmessage(ERR_get_error());
302 ctx->error = PG_HMAC_ERROR_OPENSSL;
303 return -1;
305 return 0;
309 * pg_hmac_free
311 * Free a HMAC context.
313 void
314 pg_hmac_free(pg_hmac_ctx *ctx)
316 if (ctx == NULL)
317 return;
319 HMAC_CTX_free(ctx->hmacctx);
320 #ifdef USE_RESOWNER_FOR_HMAC
321 if (ctx->resowner)
322 ResourceOwnerForgetHMAC(ctx->resowner, ctx);
323 #endif
325 explicit_bzero(ctx, sizeof(pg_hmac_ctx));
326 FREE(ctx);
330 * pg_hmac_error
332 * Returns a static string providing details about an error that happened
333 * during a HMAC computation.
335 const char *
336 pg_hmac_error(pg_hmac_ctx *ctx)
338 if (ctx == NULL)
339 return _("out of memory");
342 * If a reason is provided, rely on it, else fallback to any error code
343 * set.
345 if (ctx->errreason)
346 return ctx->errreason;
348 switch (ctx->error)
350 case PG_HMAC_ERROR_NONE:
351 return _("success");
352 case PG_HMAC_ERROR_DEST_LEN:
353 return _("destination buffer too small");
354 case PG_HMAC_ERROR_OPENSSL:
355 return _("OpenSSL failure");
358 Assert(false); /* cannot be reached */
359 return _("success");
362 /* ResourceOwner callbacks */
364 #ifdef USE_RESOWNER_FOR_HMAC
365 static void
366 ResOwnerReleaseHMAC(Datum res)
368 pg_hmac_ctx *ctx = (pg_hmac_ctx *) DatumGetPointer(res);
370 ctx->resowner = NULL;
371 pg_hmac_free(ctx);
373 #endif