Age Owner Branch data TLA 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-2025, 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 : : #include <openssl/hmac.h>
26 : :
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
35 : :
36 : : /*
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.
40 : : */
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 */
49 : :
50 : : /* Set of error states */
51 : : typedef enum pg_hmac_errno
52 : : {
53 : : PG_HMAC_ERROR_NONE = 0,
54 : : PG_HMAC_ERROR_DEST_LEN,
55 : : PG_HMAC_ERROR_OPENSSL,
56 : : } pg_hmac_errno;
57 : :
58 : : /* Internal pg_hmac_ctx structure */
59 : : struct pg_hmac_ctx
60 : : {
61 : : HMAC_CTX *hmacctx;
62 : : pg_cryptohash_type type;
63 : : pg_hmac_errno error;
64 : : const char *errreason;
65 : :
66 : : #ifdef USE_RESOWNER_FOR_HMAC
67 : : ResourceOwner resowner;
68 : : #endif
69 : : };
70 : :
71 : : /* ResourceOwner callbacks to hold HMAC contexts */
72 : : #ifdef USE_RESOWNER_FOR_HMAC
73 : : static void ResOwnerReleaseHMAC(Datum res);
74 : :
75 : : static const ResourceOwnerDesc hmac_resowner_desc =
76 : : {
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 */
82 : : };
83 : :
84 : : /* Convenience wrappers over ResourceOwnerRemember/Forget */
85 : : static inline void
668 heikki.linnakangas@i 86 :CBC 325 : ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
87 : : {
88 : 325 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
89 : 325 : }
90 : : static inline void
91 : 325 : ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
92 : : {
93 : 325 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
94 : 325 : }
95 : : #endif
96 : :
97 : : static const char *
1332 michael@paquier.xyz 98 :UBC 0 : SSLerrmessage(unsigned long ecode)
99 : : {
100 [ # # ]: 0 : if (ecode == 0)
101 : 0 : return NULL;
102 : :
103 : : /*
104 : : * This may return NULL, but we would fall back to a default error path if
105 : : * that were the case.
106 : : */
107 : 0 : return ERR_reason_error_string(ecode);
108 : : }
109 : :
110 : : /*
111 : : * pg_hmac_create
112 : : *
113 : : * Allocate a hash context. Returns NULL on failure for an OOM. The
114 : : * backend issues an error, without returning.
115 : : */
116 : : pg_hmac_ctx *
1617 michael@paquier.xyz 117 :CBC 613 : pg_hmac_create(pg_cryptohash_type type)
118 : : {
119 : : pg_hmac_ctx *ctx;
120 : :
121 : 613 : ctx = ALLOC(sizeof(pg_hmac_ctx));
122 [ - + ]: 613 : if (ctx == NULL)
1617 michael@paquier.xyz 123 :UBC 0 : return NULL;
1617 michael@paquier.xyz 124 :CBC 613 : memset(ctx, 0, sizeof(pg_hmac_ctx));
125 : :
126 : 613 : ctx->type = type;
1332 127 : 613 : ctx->error = PG_HMAC_ERROR_NONE;
128 : 613 : ctx->errreason = NULL;
129 : :
130 : :
131 : : /*
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.
135 : : */
1219 dgustafsson@postgres 136 : 613 : ERR_clear_error();
137 : :
138 : : #ifdef USE_RESOWNER_FOR_HMAC
668 heikki.linnakangas@i 139 : 325 : ResourceOwnerEnlarge(CurrentResourceOwner);
140 : : #endif
141 : :
1617 michael@paquier.xyz 142 : 613 : ctx->hmacctx = HMAC_CTX_new();
143 : :
144 [ - + ]: 613 : if (ctx->hmacctx == NULL)
145 : : {
1617 michael@paquier.xyz 146 :UBC 0 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
147 : 0 : FREE(ctx);
148 : : #ifndef FRONTEND
149 [ # # ]: 0 : ereport(ERROR,
150 : : (errcode(ERRCODE_OUT_OF_MEMORY),
151 : : errmsg("out of memory")));
152 : : #endif
153 : 0 : return NULL;
154 : : }
155 : :
156 : :
157 : : #ifdef USE_RESOWNER_FOR_HMAC
1617 michael@paquier.xyz 158 :CBC 325 : ctx->resowner = CurrentResourceOwner;
668 heikki.linnakangas@i 159 : 325 : ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
160 : : #endif
161 : :
1617 michael@paquier.xyz 162 : 613 : return ctx;
163 : : }
164 : :
165 : : /*
166 : : * pg_hmac_init
167 : : *
168 : : * Initialize a HMAC context. Returns 0 on success, -1 on failure.
169 : : */
170 : : int
171 : 514651 : pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
172 : : {
173 : 514651 : int status = 0;
174 : :
175 [ - + ]: 514651 : if (ctx == NULL)
1617 michael@paquier.xyz 176 :UBC 0 : return -1;
177 : :
1617 michael@paquier.xyz 178 [ - - - + :CBC 514651 : switch (ctx->type)
- - - ]
179 : : {
1617 michael@paquier.xyz 180 :UBC 0 : case PG_MD5:
181 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
182 : 0 : break;
183 : 0 : case PG_SHA1:
184 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
185 : 0 : break;
186 : 0 : case PG_SHA224:
187 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
188 : 0 : break;
1617 michael@paquier.xyz 189 :CBC 514651 : case PG_SHA256:
190 : 514651 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
191 : 514651 : break;
1617 michael@paquier.xyz 192 :UBC 0 : case PG_SHA384:
193 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
194 : 0 : break;
195 : 0 : case PG_SHA512:
196 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
197 : 0 : break;
198 : : }
199 : :
200 : : /* OpenSSL internals return 1 on success, 0 on failure */
1617 michael@paquier.xyz 201 [ - + ]:CBC 514651 : if (status <= 0)
202 : : {
1332 michael@paquier.xyz 203 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
204 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
1617 205 : 0 : return -1;
206 : : }
207 : :
1617 michael@paquier.xyz 208 :CBC 514651 : return 0;
209 : : }
210 : :
211 : : /*
212 : : * pg_hmac_update
213 : : *
214 : : * Update a HMAC context. Returns 0 on success, -1 on failure.
215 : : */
216 : : int
217 : 515739 : pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
218 : : {
219 : 515739 : int status = 0;
220 : :
221 [ - + ]: 515739 : if (ctx == NULL)
1617 michael@paquier.xyz 222 :UBC 0 : return -1;
223 : :
1617 michael@paquier.xyz 224 :CBC 515739 : status = HMAC_Update(ctx->hmacctx, data, len);
225 : :
226 : : /* OpenSSL internals return 1 on success, 0 on failure */
227 [ - + ]: 515739 : if (status <= 0)
228 : : {
1332 michael@paquier.xyz 229 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
230 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
1617 231 : 0 : return -1;
232 : : }
1617 michael@paquier.xyz 233 :CBC 515739 : return 0;
234 : : }
235 : :
236 : : /*
237 : : * pg_hmac_final
238 : : *
239 : : * Finalize a HMAC context. Returns 0 on success, -1 on failure.
240 : : */
241 : : int
242 : 514651 : pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
243 : : {
244 : 514651 : int status = 0;
245 : : uint32 outlen;
246 : :
247 [ - + ]: 514651 : if (ctx == NULL)
1617 michael@paquier.xyz 248 :UBC 0 : return -1;
249 : :
1617 michael@paquier.xyz 250 [ - - - + :CBC 514651 : switch (ctx->type)
- - - ]
251 : : {
1617 michael@paquier.xyz 252 :UBC 0 : case PG_MD5:
253 [ # # ]: 0 : if (len < MD5_DIGEST_LENGTH)
254 : : {
1332 255 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1617 256 : 0 : return -1;
257 : : }
258 : 0 : break;
259 : 0 : case PG_SHA1:
260 [ # # ]: 0 : if (len < SHA1_DIGEST_LENGTH)
261 : : {
1332 262 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1617 263 : 0 : return -1;
264 : : }
265 : 0 : break;
266 : 0 : case PG_SHA224:
267 [ # # ]: 0 : if (len < PG_SHA224_DIGEST_LENGTH)
268 : : {
1332 269 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1617 270 : 0 : return -1;
271 : : }
272 : 0 : break;
1617 michael@paquier.xyz 273 :CBC 514651 : case PG_SHA256:
274 [ - + ]: 514651 : if (len < PG_SHA256_DIGEST_LENGTH)
275 : : {
1332 michael@paquier.xyz 276 :UBC 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1617 277 : 0 : return -1;
278 : : }
1617 michael@paquier.xyz 279 :CBC 514651 : break;
1617 michael@paquier.xyz 280 :UBC 0 : case PG_SHA384:
281 [ # # ]: 0 : if (len < PG_SHA384_DIGEST_LENGTH)
282 : : {
1332 283 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1617 284 : 0 : return -1;
285 : : }
286 : 0 : break;
287 : 0 : case PG_SHA512:
288 [ # # ]: 0 : if (len < PG_SHA512_DIGEST_LENGTH)
289 : : {
1332 290 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1617 291 : 0 : return -1;
292 : : }
293 : 0 : break;
294 : : }
295 : :
1617 michael@paquier.xyz 296 :CBC 514651 : status = HMAC_Final(ctx->hmacctx, dest, &outlen);
297 : :
298 : : /* OpenSSL internals return 1 on success, 0 on failure */
299 [ - + ]: 514651 : if (status <= 0)
300 : : {
1332 michael@paquier.xyz 301 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
302 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
1617 303 : 0 : return -1;
304 : : }
1617 michael@paquier.xyz 305 :CBC 514651 : return 0;
306 : : }
307 : :
308 : : /*
309 : : * pg_hmac_free
310 : : *
311 : : * Free a HMAC context.
312 : : */
313 : : void
314 : 613 : pg_hmac_free(pg_hmac_ctx *ctx)
315 : : {
316 [ - + ]: 613 : if (ctx == NULL)
1617 michael@paquier.xyz 317 :UBC 0 : return;
318 : :
1617 michael@paquier.xyz 319 :CBC 613 : HMAC_CTX_free(ctx->hmacctx);
320 : : #ifdef USE_RESOWNER_FOR_HMAC
522 tgl@sss.pgh.pa.us 321 [ + - ]: 325 : if (ctx->resowner)
322 : 325 : ResourceOwnerForgetHMAC(ctx->resowner, ctx);
323 : : #endif
324 : :
1617 michael@paquier.xyz 325 : 613 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
326 : 613 : FREE(ctx);
327 : : }
328 : :
329 : : /*
330 : : * pg_hmac_error
331 : : *
332 : : * Returns a static string providing details about an error that happened
333 : : * during a HMAC computation.
334 : : */
335 : : const char *
1332 michael@paquier.xyz 336 :UBC 0 : pg_hmac_error(pg_hmac_ctx *ctx)
337 : : {
338 [ # # ]: 0 : if (ctx == NULL)
339 : 0 : return _("out of memory");
340 : :
341 : : /*
342 : : * If a reason is provided, rely on it, else fallback to any error code
343 : : * set.
344 : : */
345 [ # # ]: 0 : if (ctx->errreason)
346 : 0 : return ctx->errreason;
347 : :
348 [ # # # # ]: 0 : switch (ctx->error)
349 : : {
350 : 0 : case PG_HMAC_ERROR_NONE:
351 : 0 : return _("success");
352 : 0 : case PG_HMAC_ERROR_DEST_LEN:
353 : 0 : return _("destination buffer too small");
354 : 0 : case PG_HMAC_ERROR_OPENSSL:
355 : 0 : return _("OpenSSL failure");
356 : : }
357 : :
358 : 0 : Assert(false); /* cannot be reached */
359 : : return _("success");
360 : : }
361 : :
362 : : /* ResourceOwner callbacks */
363 : :
364 : : #ifdef USE_RESOWNER_FOR_HMAC
365 : : static void
668 heikki.linnakangas@i 366 : 0 : ResOwnerReleaseHMAC(Datum res)
367 : : {
368 : 0 : pg_hmac_ctx *ctx = (pg_hmac_ctx *) DatumGetPointer(res);
369 : :
370 : 0 : ctx->resowner = NULL;
371 : 0 : pg_hmac_free(ctx);
372 : 0 : }
373 : : #endif
|