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