Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * hmac_openssl.c
4 : : * Implementation of HMAC with OpenSSL.
5 : : *
6 : : * This should only be used if code is compiled with OpenSSL support.
7 : : *
8 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
9 : : * Portions Copyright (c) 1994, Regents of the University of California
10 : : *
11 : : * IDENTIFICATION
12 : : * src/common/hmac_openssl.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : :
17 : : #ifndef FRONTEND
18 : : #include "postgres.h"
19 : : #else
20 : : #include "postgres_fe.h"
21 : : #endif
22 : :
23 : :
24 : : #include <openssl/err.h>
25 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
26 : : #include <openssl/core_names.h>
27 : : #include <openssl/evp.h>
28 : : #include <openssl/params.h>
29 : : #else
30 : : #include <openssl/hmac.h>
31 : : #endif
32 : :
33 : : #include "common/hmac.h"
34 : : #include "common/md5.h"
35 : : #include "common/sha1.h"
36 : : #include "common/sha2.h"
37 : : #ifndef FRONTEND
38 : : #include "utils/memutils.h"
39 : : #include "utils/resowner.h"
40 : : #endif
41 : :
42 : : /*
43 : : * In backend, use an allocation in TopMemoryContext to count for resowner
44 : : * cleanup handling if necessary. In frontend, use malloc to be able to return
45 : : * a failure status back to the caller.
46 : : */
47 : : #ifndef FRONTEND
48 : : #define USE_RESOWNER_FOR_HMAC
49 : : #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
50 : : #define FREE(ptr) pfree(ptr)
51 : : #else /* FRONTEND */
52 : : #define ALLOC(size) malloc(size)
53 : : #define FREE(ptr) free(ptr)
54 : : #endif /* FRONTEND */
55 : :
56 : : /* Set of error states */
57 : : typedef enum pg_hmac_errno
58 : : {
59 : : PG_HMAC_ERROR_NONE = 0,
60 : : PG_HMAC_ERROR_DEST_LEN,
61 : : PG_HMAC_ERROR_OPENSSL,
62 : : } pg_hmac_errno;
63 : :
64 : : /* Internal pg_hmac_ctx structure */
65 : : struct pg_hmac_ctx
66 : : {
67 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
68 : : EVP_MAC *mac;
69 : : EVP_MAC_CTX *hmacctx;
70 : : #else
71 : : HMAC_CTX *hmacctx;
72 : : #endif
73 : : pg_cryptohash_type type;
74 : : pg_hmac_errno error;
75 : : const char *errreason;
76 : :
77 : : #ifdef USE_RESOWNER_FOR_HMAC
78 : : ResourceOwner resowner;
79 : : #endif
80 : : };
81 : :
82 : : /* ResourceOwner callbacks to hold HMAC contexts */
83 : : #ifdef USE_RESOWNER_FOR_HMAC
84 : : static void ResOwnerReleaseHMAC(Datum res);
85 : :
86 : : static const ResourceOwnerDesc hmac_resowner_desc =
87 : : {
88 : : .name = "OpenSSL HMAC context",
89 : : .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
90 : : .release_priority = RELEASE_PRIO_HMAC_CONTEXTS,
91 : : .ReleaseResource = ResOwnerReleaseHMAC,
92 : : .DebugPrint = NULL /* the default message is fine */
93 : : };
94 : :
95 : : /* Convenience wrappers over ResourceOwnerRemember/Forget */
96 : : static inline void
97 : 390 : ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
98 : : {
99 : 390 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
100 : 390 : }
101 : : static inline void
102 : 390 : ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
103 : : {
104 : 390 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
105 : 390 : }
106 : : #endif
107 : :
108 : : static const char *
109 : 0 : SSLerrmessage(unsigned long ecode)
110 : : {
111 [ # # ]: 0 : if (ecode == 0)
112 : 0 : return NULL;
113 : :
114 : : /*
115 : : * This may return NULL, but we would fall back to a default error path if
116 : : * that were the case.
117 : : */
118 : 0 : return ERR_reason_error_string(ecode);
119 : : }
120 : :
121 : : /*
122 : : * pg_hmac_create
123 : : *
124 : : * Allocate a hash context. Returns NULL on failure for an OOM. The
125 : : * backend issues an error, without returning.
126 : : */
127 : : pg_hmac_ctx *
128 : 711 : pg_hmac_create(pg_cryptohash_type type)
129 : : {
130 : : pg_hmac_ctx *ctx;
131 : :
132 : 711 : ctx = ALLOC(sizeof(pg_hmac_ctx));
133 [ - + ]: 711 : if (ctx == NULL)
134 : 0 : return NULL;
135 : 711 : memset(ctx, 0, sizeof(pg_hmac_ctx));
136 : :
137 : 711 : ctx->type = type;
138 : 711 : ctx->error = PG_HMAC_ERROR_NONE;
139 : 711 : ctx->errreason = NULL;
140 : :
141 : :
142 : : /*
143 : : * Initialization takes care of assigning the correct type for OpenSSL.
144 : : * Also ensure that there aren't any unconsumed errors in the queue from
145 : : * previous runs.
146 : : */
147 : 711 : ERR_clear_error();
148 : :
149 : : #ifdef USE_RESOWNER_FOR_HMAC
150 : 390 : ResourceOwnerEnlarge(CurrentResourceOwner);
151 : : #endif
152 : :
153 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
154 : :
155 : : /*
156 : : * Fetch the HMAC implementation so that it is served by the loaded
157 : : * provider.
158 : : */
159 : 711 : ctx->mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
160 [ + - ]: 711 : if (ctx->mac != NULL)
161 : 711 : ctx->hmacctx = EVP_MAC_CTX_new(ctx->mac);
162 : : #else
163 : : ctx->hmacctx = HMAC_CTX_new();
164 : : #endif
165 : :
166 [ - + ]: 711 : if (ctx->hmacctx == NULL)
167 : : {
168 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
169 : 0 : EVP_MAC_free(ctx->mac);
170 : : #endif
171 : 0 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
172 : 0 : FREE(ctx);
173 : : #ifndef FRONTEND
174 [ # # ]: 0 : ereport(ERROR,
175 : : (errcode(ERRCODE_OUT_OF_MEMORY),
176 : : errmsg("out of memory")));
177 : : #endif
178 : 0 : return NULL;
179 : : }
180 : :
181 : : #ifdef USE_RESOWNER_FOR_HMAC
182 : 390 : ctx->resowner = CurrentResourceOwner;
183 : 390 : ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
184 : : #endif
185 : :
186 : 711 : return ctx;
187 : : }
188 : :
189 : : /*
190 : : * pg_hmac_init
191 : : *
192 : : * Initialize a HMAC context. Returns 0 on success, -1 on failure.
193 : : */
194 : : int
195 : 612003 : pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
196 : : {
197 : 612003 : int status = 0;
198 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
199 : 612003 : const char *digest = NULL;
200 : : OSSL_PARAM params[2];
201 : : #endif
202 : :
203 [ - + ]: 612003 : if (ctx == NULL)
204 : 0 : return -1;
205 : :
206 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
207 [ - - - + : 612003 : switch (ctx->type)
- - - ]
208 : : {
209 : 0 : case PG_MD5:
210 : 0 : digest = "MD5";
211 : 0 : break;
212 : 0 : case PG_SHA1:
213 : 0 : digest = "SHA1";
214 : 0 : break;
215 : 0 : case PG_SHA224:
216 : 0 : digest = "SHA224";
217 : 0 : break;
218 : 612003 : case PG_SHA256:
219 : 612003 : digest = "SHA256";
220 : 612003 : break;
221 : 0 : case PG_SHA384:
222 : 0 : digest = "SHA384";
223 : 0 : break;
224 : 0 : case PG_SHA512:
225 : 0 : digest = "SHA512";
226 : 0 : break;
227 : : }
228 : :
229 : 612003 : params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
230 : : unconstify(char *, digest), 0);
231 : 612003 : params[1] = OSSL_PARAM_construct_end();
232 : :
233 : 612003 : status = EVP_MAC_init(ctx->hmacctx, key, len, params);
234 : : #else
235 : : switch (ctx->type)
236 : : {
237 : : case PG_MD5:
238 : : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
239 : : break;
240 : : case PG_SHA1:
241 : : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
242 : : break;
243 : : case PG_SHA224:
244 : : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
245 : : break;
246 : : case PG_SHA256:
247 : : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
248 : : break;
249 : : case PG_SHA384:
250 : : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
251 : : break;
252 : : case PG_SHA512:
253 : : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
254 : : break;
255 : : }
256 : : #endif
257 : :
258 : : /* OpenSSL internals return 1 on success, 0 on failure */
259 [ - + ]: 612003 : if (status <= 0)
260 : : {
261 : 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
262 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
263 : 0 : return -1;
264 : : }
265 : :
266 : 612003 : return 0;
267 : : }
268 : :
269 : : /*
270 : : * pg_hmac_update
271 : : *
272 : : * Update a HMAC context. Returns 0 on success, -1 on failure.
273 : : */
274 : : int
275 : 613221 : pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
276 : : {
277 : 613221 : int status = 0;
278 : :
279 [ - + ]: 613221 : if (ctx == NULL)
280 : 0 : return -1;
281 : :
282 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
283 : 613221 : status = EVP_MAC_update(ctx->hmacctx, data, len);
284 : : #else
285 : : status = HMAC_Update(ctx->hmacctx, data, len);
286 : : #endif
287 : :
288 : : /* OpenSSL internals return 1 on success, 0 on failure */
289 [ - + ]: 613221 : if (status <= 0)
290 : : {
291 : 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
292 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
293 : 0 : return -1;
294 : : }
295 : 613221 : return 0;
296 : : }
297 : :
298 : : /*
299 : : * pg_hmac_final
300 : : *
301 : : * Finalize a HMAC context. Returns 0 on success, -1 on failure.
302 : : */
303 : : int
304 : 612003 : pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
305 : : {
306 : 612003 : int status = 0;
307 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
308 : : size_t outlen;
309 : : #else
310 : : uint32 outlen;
311 : : #endif
312 : :
313 [ - + ]: 612003 : if (ctx == NULL)
314 : 0 : return -1;
315 : :
316 [ - - - + : 612003 : switch (ctx->type)
- - - ]
317 : : {
318 : 0 : case PG_MD5:
319 [ # # ]: 0 : if (len < MD5_DIGEST_LENGTH)
320 : : {
321 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
322 : 0 : return -1;
323 : : }
324 : 0 : break;
325 : 0 : case PG_SHA1:
326 [ # # ]: 0 : if (len < SHA1_DIGEST_LENGTH)
327 : : {
328 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
329 : 0 : return -1;
330 : : }
331 : 0 : break;
332 : 0 : case PG_SHA224:
333 [ # # ]: 0 : if (len < PG_SHA224_DIGEST_LENGTH)
334 : : {
335 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
336 : 0 : return -1;
337 : : }
338 : 0 : break;
339 : 612003 : case PG_SHA256:
340 [ - + ]: 612003 : if (len < PG_SHA256_DIGEST_LENGTH)
341 : : {
342 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
343 : 0 : return -1;
344 : : }
345 : 612003 : break;
346 : 0 : case PG_SHA384:
347 [ # # ]: 0 : if (len < PG_SHA384_DIGEST_LENGTH)
348 : : {
349 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
350 : 0 : return -1;
351 : : }
352 : 0 : break;
353 : 0 : case PG_SHA512:
354 [ # # ]: 0 : if (len < PG_SHA512_DIGEST_LENGTH)
355 : : {
356 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
357 : 0 : return -1;
358 : : }
359 : 0 : break;
360 : : }
361 : :
362 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
363 : 612003 : status = EVP_MAC_final(ctx->hmacctx, dest, &outlen, len);
364 : : #else
365 : : status = HMAC_Final(ctx->hmacctx, dest, &outlen);
366 : : #endif
367 : :
368 : : /* OpenSSL internals return 1 on success, 0 on failure */
369 [ - + ]: 612003 : if (status <= 0)
370 : : {
371 : 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
372 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
373 : 0 : return -1;
374 : : }
375 : 612003 : return 0;
376 : : }
377 : :
378 : : /*
379 : : * pg_hmac_free
380 : : *
381 : : * Free a HMAC context.
382 : : */
383 : : void
384 : 711 : pg_hmac_free(pg_hmac_ctx *ctx)
385 : : {
386 [ - + ]: 711 : if (ctx == NULL)
387 : 0 : return;
388 : :
389 : : #if OPENSSL_VERSION_NUMBER >= 0x30000000L
390 : 711 : EVP_MAC_CTX_free(ctx->hmacctx);
391 : 711 : EVP_MAC_free(ctx->mac);
392 : : #else
393 : : HMAC_CTX_free(ctx->hmacctx);
394 : : #endif
395 : :
396 : : #ifdef USE_RESOWNER_FOR_HMAC
397 [ + - ]: 390 : if (ctx->resowner)
398 : 390 : ResourceOwnerForgetHMAC(ctx->resowner, ctx);
399 : : #endif
400 : :
401 : 711 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
402 : 711 : FREE(ctx);
403 : : }
404 : :
405 : : /*
406 : : * pg_hmac_error
407 : : *
408 : : * Returns a static string providing details about an error that happened
409 : : * during a HMAC computation.
410 : : */
411 : : const char *
412 : 0 : pg_hmac_error(pg_hmac_ctx *ctx)
413 : : {
414 [ # # ]: 0 : if (ctx == NULL)
415 : 0 : return _("out of memory");
416 : :
417 : : /*
418 : : * If a reason is provided, rely on it, else fallback to any error code
419 : : * set.
420 : : */
421 [ # # ]: 0 : if (ctx->errreason)
422 : 0 : return ctx->errreason;
423 : :
424 [ # # # # ]: 0 : switch (ctx->error)
425 : : {
426 : 0 : case PG_HMAC_ERROR_NONE:
427 : 0 : return _("success");
428 : 0 : case PG_HMAC_ERROR_DEST_LEN:
429 : 0 : return _("destination buffer too small");
430 : 0 : case PG_HMAC_ERROR_OPENSSL:
431 : 0 : return _("OpenSSL failure");
432 : : }
433 : :
434 : : Assert(false); /* cannot be reached */
435 : 0 : return _("success");
436 : : }
437 : :
438 : : /* ResourceOwner callbacks */
439 : :
440 : : #ifdef USE_RESOWNER_FOR_HMAC
441 : : static void
442 : 0 : ResOwnerReleaseHMAC(Datum res)
443 : : {
444 : 0 : pg_hmac_ctx *ctx = (pg_hmac_ctx *) DatumGetPointer(res);
445 : :
446 : 0 : ctx->resowner = NULL;
447 : 0 : pg_hmac_free(ctx);
448 : 0 : }
449 : : #endif
|