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-2024, 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. For versions of OpenSSL where HMAC_CTX is
39 : : * known, just use palloc(). In frontend, use malloc to be able to return
40 : : * a failure status back to the caller.
41 : : */
42 : : #ifndef FRONTEND
43 : : #ifdef HAVE_HMAC_CTX_NEW
44 : : #define USE_RESOWNER_FOR_HMAC
45 : : #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
46 : : #else
47 : : #define ALLOC(size) palloc(size)
48 : : #endif
49 : : #define FREE(ptr) pfree(ptr)
50 : : #else /* FRONTEND */
51 : : #define ALLOC(size) malloc(size)
52 : : #define FREE(ptr) free(ptr)
53 : : #endif /* FRONTEND */
54 : :
55 : : /* Set of error states */
56 : : typedef enum pg_hmac_errno
57 : : {
58 : : PG_HMAC_ERROR_NONE = 0,
59 : : PG_HMAC_ERROR_DEST_LEN,
60 : : PG_HMAC_ERROR_OPENSSL,
61 : : } pg_hmac_errno;
62 : :
63 : : /* Internal pg_hmac_ctx structure */
64 : : struct pg_hmac_ctx
65 : : {
66 : : HMAC_CTX *hmacctx;
67 : : pg_cryptohash_type type;
68 : : pg_hmac_errno error;
69 : : const char *errreason;
70 : :
71 : : #ifdef USE_RESOWNER_FOR_HMAC
72 : : ResourceOwner resowner;
73 : : #endif
74 : : };
75 : :
76 : : /* ResourceOwner callbacks to hold HMAC contexts */
77 : : #ifdef USE_RESOWNER_FOR_HMAC
78 : : static void ResOwnerReleaseHMAC(Datum res);
79 : :
80 : : static const ResourceOwnerDesc hmac_resowner_desc =
81 : : {
82 : : .name = "OpenSSL HMAC context",
83 : : .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
84 : : .release_priority = RELEASE_PRIO_HMAC_CONTEXTS,
85 : : .ReleaseResource = ResOwnerReleaseHMAC,
86 : : .DebugPrint = NULL /* the default message is fine */
87 : : };
88 : :
89 : : /* Convenience wrappers over ResourceOwnerRemember/Forget */
90 : : static inline void
158 heikki.linnakangas@i 91 :GNC 256 : ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
92 : : {
93 : 256 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
94 : 256 : }
95 : : static inline void
96 : 256 : ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
97 : : {
98 : 256 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
99 : 256 : }
100 : : #endif
101 : :
102 : : static const char *
822 michael@paquier.xyz 103 :UBC 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_hmac_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_hmac_ctx *
1107 michael@paquier.xyz 122 :CBC 462 : pg_hmac_create(pg_cryptohash_type type)
123 : : {
124 : : pg_hmac_ctx *ctx;
125 : :
126 : 462 : ctx = ALLOC(sizeof(pg_hmac_ctx));
127 [ - + ]: 462 : if (ctx == NULL)
1107 michael@paquier.xyz 128 :UBC 0 : return NULL;
1107 michael@paquier.xyz 129 :CBC 462 : memset(ctx, 0, sizeof(pg_hmac_ctx));
130 : :
131 : 462 : ctx->type = type;
822 132 : 462 : ctx->error = PG_HMAC_ERROR_NONE;
133 : 462 : ctx->errreason = NULL;
134 : :
135 : :
136 : : /*
137 : : * Initialization takes care of assigning the correct type for OpenSSL.
138 : : * Also ensure that there aren't any unconsumed errors in the queue from
139 : : * previous runs.
140 : : */
709 dgustafsson@postgres 141 : 462 : ERR_clear_error();
142 : :
143 : : #ifdef USE_RESOWNER_FOR_HMAC
158 heikki.linnakangas@i 144 :GNC 256 : ResourceOwnerEnlarge(CurrentResourceOwner);
145 : : #endif
146 : :
147 : : #ifdef HAVE_HMAC_CTX_NEW
1107 michael@paquier.xyz 148 :CBC 462 : ctx->hmacctx = HMAC_CTX_new();
149 : : #else
150 : : ctx->hmacctx = ALLOC(sizeof(HMAC_CTX));
151 : : #endif
152 : :
153 [ - + ]: 462 : if (ctx->hmacctx == NULL)
154 : : {
1107 michael@paquier.xyz 155 :UBC 0 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
156 : 0 : FREE(ctx);
157 : : #ifndef FRONTEND
158 [ # # ]: 0 : ereport(ERROR,
159 : : (errcode(ERRCODE_OUT_OF_MEMORY),
160 : : errmsg("out of memory")));
161 : : #endif
162 : 0 : return NULL;
163 : : }
164 : :
165 : : #ifndef HAVE_HMAC_CTX_NEW
166 : : memset(ctx->hmacctx, 0, sizeof(HMAC_CTX));
167 : : #endif
168 : :
169 : : #ifdef USE_RESOWNER_FOR_HMAC
1107 michael@paquier.xyz 170 :GNC 256 : ctx->resowner = CurrentResourceOwner;
158 heikki.linnakangas@i 171 : 256 : ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
172 : : #endif
173 : :
1107 michael@paquier.xyz 174 :CBC 462 : return ctx;
175 : : }
176 : :
177 : : /*
178 : : * pg_hmac_init
179 : : *
180 : : * Initialize a HMAC context. Returns 0 on success, -1 on failure.
181 : : */
182 : : int
183 : 403935 : pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
184 : : {
185 : 403935 : int status = 0;
186 : :
187 [ - + ]: 403935 : if (ctx == NULL)
1107 michael@paquier.xyz 188 :UBC 0 : return -1;
189 : :
1107 michael@paquier.xyz 190 [ - - - + :CBC 403935 : switch (ctx->type)
- - - ]
191 : : {
1107 michael@paquier.xyz 192 :UBC 0 : case PG_MD5:
193 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
194 : 0 : break;
195 : 0 : case PG_SHA1:
196 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
197 : 0 : break;
198 : 0 : case PG_SHA224:
199 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
200 : 0 : break;
1107 michael@paquier.xyz 201 :CBC 403935 : case PG_SHA256:
202 : 403935 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
203 : 403935 : break;
1107 michael@paquier.xyz 204 :UBC 0 : case PG_SHA384:
205 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
206 : 0 : break;
207 : 0 : case PG_SHA512:
208 : 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
209 : 0 : break;
210 : : }
211 : :
212 : : /* OpenSSL internals return 1 on success, 0 on failure */
1107 michael@paquier.xyz 213 [ - + ]:CBC 403935 : if (status <= 0)
214 : : {
822 michael@paquier.xyz 215 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
216 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
1107 217 : 0 : return -1;
218 : : }
219 : :
1107 michael@paquier.xyz 220 :CBC 403935 : return 0;
221 : : }
222 : :
223 : : /*
224 : : * pg_hmac_update
225 : : *
226 : : * Update a HMAC context. Returns 0 on success, -1 on failure.
227 : : */
228 : : int
229 : 404684 : pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
230 : : {
231 : 404684 : int status = 0;
232 : :
233 [ - + ]: 404684 : if (ctx == NULL)
1107 michael@paquier.xyz 234 :UBC 0 : return -1;
235 : :
1107 michael@paquier.xyz 236 :CBC 404684 : status = HMAC_Update(ctx->hmacctx, data, len);
237 : :
238 : : /* OpenSSL internals return 1 on success, 0 on failure */
239 [ - + ]: 404684 : if (status <= 0)
240 : : {
822 michael@paquier.xyz 241 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
242 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
1107 243 : 0 : return -1;
244 : : }
1107 michael@paquier.xyz 245 :CBC 404684 : return 0;
246 : : }
247 : :
248 : : /*
249 : : * pg_hmac_final
250 : : *
251 : : * Finalize a HMAC context. Returns 0 on success, -1 on failure.
252 : : */
253 : : int
254 : 403935 : pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
255 : : {
256 : 403935 : int status = 0;
257 : : uint32 outlen;
258 : :
259 [ - + ]: 403935 : if (ctx == NULL)
1107 michael@paquier.xyz 260 :UBC 0 : return -1;
261 : :
1107 michael@paquier.xyz 262 [ - - - + :CBC 403935 : switch (ctx->type)
- - - ]
263 : : {
1107 michael@paquier.xyz 264 :UBC 0 : case PG_MD5:
265 [ # # ]: 0 : if (len < MD5_DIGEST_LENGTH)
266 : : {
822 267 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1107 268 : 0 : return -1;
269 : : }
270 : 0 : break;
271 : 0 : case PG_SHA1:
272 [ # # ]: 0 : if (len < SHA1_DIGEST_LENGTH)
273 : : {
822 274 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1107 275 : 0 : return -1;
276 : : }
277 : 0 : break;
278 : 0 : case PG_SHA224:
279 [ # # ]: 0 : if (len < PG_SHA224_DIGEST_LENGTH)
280 : : {
822 281 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1107 282 : 0 : return -1;
283 : : }
284 : 0 : break;
1107 michael@paquier.xyz 285 :CBC 403935 : case PG_SHA256:
286 [ - + ]: 403935 : if (len < PG_SHA256_DIGEST_LENGTH)
287 : : {
822 michael@paquier.xyz 288 :UBC 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1107 289 : 0 : return -1;
290 : : }
1107 michael@paquier.xyz 291 :CBC 403935 : break;
1107 michael@paquier.xyz 292 :UBC 0 : case PG_SHA384:
293 [ # # ]: 0 : if (len < PG_SHA384_DIGEST_LENGTH)
294 : : {
822 295 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1107 296 : 0 : return -1;
297 : : }
298 : 0 : break;
299 : 0 : case PG_SHA512:
300 [ # # ]: 0 : if (len < PG_SHA512_DIGEST_LENGTH)
301 : : {
822 302 : 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
1107 303 : 0 : return -1;
304 : : }
305 : 0 : break;
306 : : }
307 : :
1107 michael@paquier.xyz 308 :CBC 403935 : status = HMAC_Final(ctx->hmacctx, dest, &outlen);
309 : :
310 : : /* OpenSSL internals return 1 on success, 0 on failure */
311 [ - + ]: 403935 : if (status <= 0)
312 : : {
822 michael@paquier.xyz 313 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
314 : 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
1107 315 : 0 : return -1;
316 : : }
1107 michael@paquier.xyz 317 :CBC 403935 : return 0;
318 : : }
319 : :
320 : : /*
321 : : * pg_hmac_free
322 : : *
323 : : * Free a HMAC context.
324 : : */
325 : : void
326 : 462 : pg_hmac_free(pg_hmac_ctx *ctx)
327 : : {
328 [ - + ]: 462 : if (ctx == NULL)
1107 michael@paquier.xyz 329 :UBC 0 : return;
330 : :
331 : : #ifdef HAVE_HMAC_CTX_FREE
1107 michael@paquier.xyz 332 :CBC 462 : HMAC_CTX_free(ctx->hmacctx);
333 : : #else
334 : : explicit_bzero(ctx->hmacctx, sizeof(HMAC_CTX));
335 : : FREE(ctx->hmacctx);
336 : : #endif
337 : :
338 : : #ifdef USE_RESOWNER_FOR_HMAC
12 tgl@sss.pgh.pa.us 339 [ + - ]:GNC 256 : if (ctx->resowner)
340 : 256 : ResourceOwnerForgetHMAC(ctx->resowner, ctx);
341 : : #endif
342 : :
1107 michael@paquier.xyz 343 :CBC 462 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
344 : 462 : FREE(ctx);
345 : : }
346 : :
347 : : /*
348 : : * pg_hmac_error
349 : : *
350 : : * Returns a static string providing details about an error that happened
351 : : * during a HMAC computation.
352 : : */
353 : : const char *
822 michael@paquier.xyz 354 :UBC 0 : pg_hmac_error(pg_hmac_ctx *ctx)
355 : : {
356 [ # # ]: 0 : if (ctx == NULL)
357 : 0 : return _("out of memory");
358 : :
359 : : /*
360 : : * If a reason is provided, rely on it, else fallback to any error code
361 : : * set.
362 : : */
363 [ # # ]: 0 : if (ctx->errreason)
364 : 0 : return ctx->errreason;
365 : :
366 [ # # # # ]: 0 : switch (ctx->error)
367 : : {
368 : 0 : case PG_HMAC_ERROR_NONE:
369 : 0 : return _("success");
370 : 0 : case PG_HMAC_ERROR_DEST_LEN:
371 : 0 : return _("destination buffer too small");
372 : 0 : case PG_HMAC_ERROR_OPENSSL:
373 : 0 : return _("OpenSSL failure");
374 : : }
375 : :
376 : 0 : Assert(false); /* cannot be reached */
377 : : return _("success");
378 : : }
379 : :
380 : : /* ResourceOwner callbacks */
381 : :
382 : : #ifdef USE_RESOWNER_FOR_HMAC
383 : : static void
158 heikki.linnakangas@i 384 :UNC 0 : ResOwnerReleaseHMAC(Datum res)
385 : : {
386 : 0 : pg_hmac_ctx *ctx = (pg_hmac_ctx *) DatumGetPointer(res);
387 : :
388 : 0 : ctx->resowner = NULL;
389 : 0 : pg_hmac_free(ctx);
390 : 0 : }
391 : : #endif
|