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. 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
86 536 : ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
87 : {
88 536 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
89 536 : }
90 : static inline void
91 536 : ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
92 : {
93 536 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
94 536 : }
95 : #endif
96 :
97 : static const char *
98 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 *
117 918 : pg_hmac_create(pg_cryptohash_type type)
118 : {
119 : pg_hmac_ctx *ctx;
120 :
121 918 : ctx = ALLOC(sizeof(pg_hmac_ctx));
122 918 : if (ctx == NULL)
123 0 : return NULL;
124 918 : memset(ctx, 0, sizeof(pg_hmac_ctx));
125 :
126 918 : ctx->type = type;
127 918 : ctx->error = PG_HMAC_ERROR_NONE;
128 918 : 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 : */
136 918 : ERR_clear_error();
137 :
138 : #ifdef USE_RESOWNER_FOR_HMAC
139 536 : ResourceOwnerEnlarge(CurrentResourceOwner);
140 : #endif
141 :
142 918 : ctx->hmacctx = HMAC_CTX_new();
143 :
144 918 : if (ctx->hmacctx == NULL)
145 : {
146 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
158 536 : ctx->resowner = CurrentResourceOwner;
159 536 : ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx);
160 : #endif
161 :
162 918 : 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 848814 : pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
172 : {
173 848814 : int status = 0;
174 :
175 848814 : if (ctx == NULL)
176 0 : return -1;
177 :
178 848814 : switch (ctx->type)
179 : {
180 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;
189 848814 : case PG_SHA256:
190 848814 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
191 848814 : break;
192 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 */
201 848814 : if (status <= 0)
202 : {
203 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
204 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
205 0 : return -1;
206 : }
207 :
208 848814 : 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 850226 : pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
218 : {
219 850226 : int status = 0;
220 :
221 850226 : if (ctx == NULL)
222 0 : return -1;
223 :
224 850226 : status = HMAC_Update(ctx->hmacctx, data, len);
225 :
226 : /* OpenSSL internals return 1 on success, 0 on failure */
227 850226 : if (status <= 0)
228 : {
229 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
230 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
231 0 : return -1;
232 : }
233 850226 : 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 848814 : pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
243 : {
244 848814 : int status = 0;
245 : uint32 outlen;
246 :
247 848814 : if (ctx == NULL)
248 0 : return -1;
249 :
250 848814 : switch (ctx->type)
251 : {
252 0 : case PG_MD5:
253 0 : if (len < MD5_DIGEST_LENGTH)
254 : {
255 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
256 0 : return -1;
257 : }
258 0 : break;
259 0 : case PG_SHA1:
260 0 : if (len < SHA1_DIGEST_LENGTH)
261 : {
262 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
263 0 : return -1;
264 : }
265 0 : break;
266 0 : case PG_SHA224:
267 0 : if (len < PG_SHA224_DIGEST_LENGTH)
268 : {
269 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
270 0 : return -1;
271 : }
272 0 : break;
273 848814 : case PG_SHA256:
274 848814 : if (len < PG_SHA256_DIGEST_LENGTH)
275 : {
276 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
277 0 : return -1;
278 : }
279 848814 : break;
280 0 : case PG_SHA384:
281 0 : if (len < PG_SHA384_DIGEST_LENGTH)
282 : {
283 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
284 0 : return -1;
285 : }
286 0 : break;
287 0 : case PG_SHA512:
288 0 : if (len < PG_SHA512_DIGEST_LENGTH)
289 : {
290 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
291 0 : return -1;
292 : }
293 0 : break;
294 : }
295 :
296 848814 : status = HMAC_Final(ctx->hmacctx, dest, &outlen);
297 :
298 : /* OpenSSL internals return 1 on success, 0 on failure */
299 848814 : if (status <= 0)
300 : {
301 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
302 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
303 0 : return -1;
304 : }
305 848814 : return 0;
306 : }
307 :
308 : /*
309 : * pg_hmac_free
310 : *
311 : * Free a HMAC context.
312 : */
313 : void
314 918 : pg_hmac_free(pg_hmac_ctx *ctx)
315 : {
316 918 : if (ctx == NULL)
317 0 : return;
318 :
319 918 : HMAC_CTX_free(ctx->hmacctx);
320 : #ifdef USE_RESOWNER_FOR_HMAC
321 536 : if (ctx->resowner)
322 536 : ResourceOwnerForgetHMAC(ctx->resowner, ctx);
323 : #endif
324 :
325 918 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
326 918 : 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 *
336 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 : Assert(false); /* cannot be reached */
359 0 : return _("success");
360 : }
361 :
362 : /* ResourceOwner callbacks */
363 :
364 : #ifdef USE_RESOWNER_FOR_HMAC
365 : static void
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
|