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
91 512 : ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
92 : {
93 512 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
94 512 : }
95 : static inline void
96 512 : ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
97 : {
98 512 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
99 512 : }
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_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 *
122 894 : pg_hmac_create(pg_cryptohash_type type)
123 : {
124 : pg_hmac_ctx *ctx;
125 :
126 894 : ctx = ALLOC(sizeof(pg_hmac_ctx));
127 894 : if (ctx == NULL)
128 0 : return NULL;
129 894 : memset(ctx, 0, sizeof(pg_hmac_ctx));
130 :
131 894 : ctx->type = type;
132 894 : ctx->error = PG_HMAC_ERROR_NONE;
133 894 : 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 : */
141 894 : ERR_clear_error();
142 :
143 : #ifdef USE_RESOWNER_FOR_HMAC
144 512 : ResourceOwnerEnlarge(CurrentResourceOwner);
145 : #endif
146 :
147 : #ifdef HAVE_HMAC_CTX_NEW
148 894 : ctx->hmacctx = HMAC_CTX_new();
149 : #else
150 : ctx->hmacctx = ALLOC(sizeof(HMAC_CTX));
151 : #endif
152 :
153 894 : if (ctx->hmacctx == NULL)
154 : {
155 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
170 512 : ctx->resowner = CurrentResourceOwner;
171 512 : ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
172 : #endif
173 :
174 894 : 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 799650 : pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
184 : {
185 799650 : int status = 0;
186 :
187 799650 : if (ctx == NULL)
188 0 : return -1;
189 :
190 799650 : switch (ctx->type)
191 : {
192 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;
201 799650 : case PG_SHA256:
202 799650 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
203 799650 : break;
204 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 */
213 799650 : if (status <= 0)
214 : {
215 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
216 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
217 0 : return -1;
218 : }
219 :
220 799650 : 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 801050 : pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
230 : {
231 801050 : int status = 0;
232 :
233 801050 : if (ctx == NULL)
234 0 : return -1;
235 :
236 801050 : status = HMAC_Update(ctx->hmacctx, data, len);
237 :
238 : /* OpenSSL internals return 1 on success, 0 on failure */
239 801050 : if (status <= 0)
240 : {
241 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
242 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
243 0 : return -1;
244 : }
245 801050 : 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 799650 : pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
255 : {
256 799650 : int status = 0;
257 : uint32 outlen;
258 :
259 799650 : if (ctx == NULL)
260 0 : return -1;
261 :
262 799650 : switch (ctx->type)
263 : {
264 0 : case PG_MD5:
265 0 : if (len < MD5_DIGEST_LENGTH)
266 : {
267 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
268 0 : return -1;
269 : }
270 0 : break;
271 0 : case PG_SHA1:
272 0 : if (len < SHA1_DIGEST_LENGTH)
273 : {
274 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
275 0 : return -1;
276 : }
277 0 : break;
278 0 : case PG_SHA224:
279 0 : if (len < PG_SHA224_DIGEST_LENGTH)
280 : {
281 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
282 0 : return -1;
283 : }
284 0 : break;
285 799650 : case PG_SHA256:
286 799650 : if (len < PG_SHA256_DIGEST_LENGTH)
287 : {
288 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
289 0 : return -1;
290 : }
291 799650 : break;
292 0 : case PG_SHA384:
293 0 : if (len < PG_SHA384_DIGEST_LENGTH)
294 : {
295 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
296 0 : return -1;
297 : }
298 0 : break;
299 0 : case PG_SHA512:
300 0 : if (len < PG_SHA512_DIGEST_LENGTH)
301 : {
302 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
303 0 : return -1;
304 : }
305 0 : break;
306 : }
307 :
308 799650 : status = HMAC_Final(ctx->hmacctx, dest, &outlen);
309 :
310 : /* OpenSSL internals return 1 on success, 0 on failure */
311 799650 : if (status <= 0)
312 : {
313 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
314 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
315 0 : return -1;
316 : }
317 799650 : return 0;
318 : }
319 :
320 : /*
321 : * pg_hmac_free
322 : *
323 : * Free a HMAC context.
324 : */
325 : void
326 894 : pg_hmac_free(pg_hmac_ctx *ctx)
327 : {
328 894 : if (ctx == NULL)
329 0 : return;
330 :
331 : #ifdef HAVE_HMAC_CTX_FREE
332 894 : 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
339 512 : if (ctx->resowner)
340 512 : ResourceOwnerForgetHMAC(ctx->resowner, ctx);
341 : #endif
342 :
343 894 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
344 894 : 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 *
354 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 : Assert(false); /* cannot be reached */
377 0 : return _("success");
378 : }
379 :
380 : /* ResourceOwner callbacks */
381 :
382 : #ifdef USE_RESOWNER_FOR_HMAC
383 : static void
384 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
|