Age Owner 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-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 : #include "utils/resowner_private.h"
35 : #endif
36 :
37 : /*
38 : * In backend, use an allocation in TopMemoryContext to count for resowner
39 : * cleanup handling if necessary. For versions of OpenSSL where HMAC_CTX is
40 : * known, just use palloc(). In frontend, use malloc to be able to return
41 : * a failure status back to the caller.
42 : */
43 : #ifndef FRONTEND
44 : #ifdef HAVE_HMAC_CTX_NEW
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 : #ifndef FRONTEND
72 : ResourceOwner resowner;
73 : #endif
74 : };
75 :
76 : static const char *
451 michael 77 UBC 0 : SSLerrmessage(unsigned long ecode)
78 : {
79 0 : if (ecode == 0)
80 0 : return NULL;
81 :
82 : /*
83 : * This may return NULL, but we would fall back to a default error path if
84 : * that were the case.
85 : */
86 0 : return ERR_reason_error_string(ecode);
87 : }
88 :
89 : /*
90 : * pg_hmac_create
91 : *
92 : * Allocate a hash context. Returns NULL on failure for an OOM. The
93 : * backend issues an error, without returning.
94 : */
95 : pg_hmac_ctx *
736 michael 96 CBC 377 : pg_hmac_create(pg_cryptohash_type type)
97 : {
98 : pg_hmac_ctx *ctx;
99 :
100 377 : ctx = ALLOC(sizeof(pg_hmac_ctx));
101 377 : if (ctx == NULL)
736 michael 102 UBC 0 : return NULL;
736 michael 103 CBC 377 : memset(ctx, 0, sizeof(pg_hmac_ctx));
104 :
105 377 : ctx->type = type;
451 106 377 : ctx->error = PG_HMAC_ERROR_NONE;
107 377 : ctx->errreason = NULL;
108 :
109 :
110 : /*
111 : * Initialization takes care of assigning the correct type for OpenSSL.
112 : * Also ensure that there aren't any unconsumed errors in the queue from
113 : * previous runs.
114 : */
338 dgustafsson 115 377 : ERR_clear_error();
116 : #ifdef HAVE_HMAC_CTX_NEW
117 : #ifndef FRONTEND
736 michael 118 216 : ResourceOwnerEnlargeHMAC(CurrentResourceOwner);
119 : #endif
120 377 : ctx->hmacctx = HMAC_CTX_new();
121 : #else
122 : ctx->hmacctx = ALLOC(sizeof(HMAC_CTX));
123 : #endif
124 :
125 377 : if (ctx->hmacctx == NULL)
126 : {
736 michael 127 UBC 0 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
128 0 : FREE(ctx);
129 : #ifndef FRONTEND
130 0 : ereport(ERROR,
131 : (errcode(ERRCODE_OUT_OF_MEMORY),
132 : errmsg("out of memory")));
133 : #endif
134 0 : return NULL;
135 : }
136 :
137 : #ifdef HAVE_HMAC_CTX_NEW
138 : #ifndef FRONTEND
736 michael 139 CBC 216 : ctx->resowner = CurrentResourceOwner;
140 216 : ResourceOwnerRememberHMAC(CurrentResourceOwner, PointerGetDatum(ctx));
141 : #endif
142 : #else
143 : memset(ctx->hmacctx, 0, sizeof(HMAC_CTX));
144 : #endif /* HAVE_HMAC_CTX_NEW */
145 :
146 377 : return ctx;
147 : }
148 :
149 : /*
150 : * pg_hmac_init
151 : *
152 : * Initialize a HMAC context. Returns 0 on success, -1 on failure.
153 : */
154 : int
155 334235 : pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
156 : {
157 334235 : int status = 0;
158 :
159 334235 : if (ctx == NULL)
736 michael 160 UBC 0 : return -1;
161 :
736 michael 162 CBC 334235 : switch (ctx->type)
163 : {
736 michael 164 UBC 0 : case PG_MD5:
165 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
166 0 : break;
167 0 : case PG_SHA1:
168 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
169 0 : break;
170 0 : case PG_SHA224:
171 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
172 0 : break;
736 michael 173 CBC 334235 : case PG_SHA256:
174 334235 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
175 334235 : break;
736 michael 176 UBC 0 : case PG_SHA384:
177 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
178 0 : break;
179 0 : case PG_SHA512:
180 0 : status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
181 0 : break;
182 : }
183 :
184 : /* OpenSSL internals return 1 on success, 0 on failure */
736 michael 185 CBC 334235 : if (status <= 0)
186 : {
451 michael 187 UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
188 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
736 189 0 : return -1;
190 : }
191 :
736 michael 192 CBC 334235 : return 0;
193 : }
194 :
195 : /*
196 : * pg_hmac_update
197 : *
198 : * Update a HMAC context. Returns 0 on success, -1 on failure.
199 : */
200 : int
201 334815 : pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
202 : {
203 334815 : int status = 0;
204 :
205 334815 : if (ctx == NULL)
736 michael 206 UBC 0 : return -1;
207 :
736 michael 208 CBC 334815 : status = HMAC_Update(ctx->hmacctx, data, len);
209 :
210 : /* OpenSSL internals return 1 on success, 0 on failure */
211 334815 : if (status <= 0)
212 : {
451 michael 213 UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
214 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
736 215 0 : return -1;
216 : }
736 michael 217 CBC 334815 : return 0;
218 : }
219 :
220 : /*
221 : * pg_hmac_final
222 : *
223 : * Finalize a HMAC context. Returns 0 on success, -1 on failure.
224 : */
225 : int
226 334235 : pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
227 : {
228 334235 : int status = 0;
229 : uint32 outlen;
230 :
231 334235 : if (ctx == NULL)
736 michael 232 UBC 0 : return -1;
233 :
736 michael 234 CBC 334235 : switch (ctx->type)
235 : {
736 michael 236 UBC 0 : case PG_MD5:
237 0 : if (len < MD5_DIGEST_LENGTH)
238 : {
451 239 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
736 240 0 : return -1;
241 : }
242 0 : break;
243 0 : case PG_SHA1:
244 0 : if (len < SHA1_DIGEST_LENGTH)
245 : {
451 246 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
736 247 0 : return -1;
248 : }
249 0 : break;
250 0 : case PG_SHA224:
251 0 : if (len < PG_SHA224_DIGEST_LENGTH)
252 : {
451 253 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
736 254 0 : return -1;
255 : }
256 0 : break;
736 michael 257 CBC 334235 : case PG_SHA256:
258 334235 : if (len < PG_SHA256_DIGEST_LENGTH)
259 : {
451 michael 260 UBC 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
736 261 0 : return -1;
262 : }
736 michael 263 CBC 334235 : break;
736 michael 264 UBC 0 : case PG_SHA384:
265 0 : if (len < PG_SHA384_DIGEST_LENGTH)
266 : {
451 267 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
736 268 0 : return -1;
269 : }
270 0 : break;
271 0 : case PG_SHA512:
272 0 : if (len < PG_SHA512_DIGEST_LENGTH)
273 : {
451 274 0 : ctx->error = PG_HMAC_ERROR_DEST_LEN;
736 275 0 : return -1;
276 : }
277 0 : break;
278 : }
279 :
736 michael 280 CBC 334235 : status = HMAC_Final(ctx->hmacctx, dest, &outlen);
281 :
282 : /* OpenSSL internals return 1 on success, 0 on failure */
283 334235 : if (status <= 0)
284 : {
451 michael 285 UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
286 0 : ctx->error = PG_HMAC_ERROR_OPENSSL;
736 287 0 : return -1;
288 : }
736 michael 289 CBC 334235 : return 0;
290 : }
291 :
292 : /*
293 : * pg_hmac_free
294 : *
295 : * Free a HMAC context.
296 : */
297 : void
298 377 : pg_hmac_free(pg_hmac_ctx *ctx)
299 : {
300 377 : if (ctx == NULL)
736 michael 301 UBC 0 : return;
302 :
303 : #ifdef HAVE_HMAC_CTX_FREE
736 michael 304 CBC 377 : HMAC_CTX_free(ctx->hmacctx);
305 : #ifndef FRONTEND
306 216 : ResourceOwnerForgetHMAC(ctx->resowner, PointerGetDatum(ctx));
307 : #endif
308 : #else
309 : explicit_bzero(ctx->hmacctx, sizeof(HMAC_CTX));
310 : FREE(ctx->hmacctx);
311 : #endif
312 :
313 377 : explicit_bzero(ctx, sizeof(pg_hmac_ctx));
314 377 : FREE(ctx);
315 : }
316 :
317 : /*
318 : * pg_hmac_error
319 : *
320 : * Returns a static string providing details about an error that happened
321 : * during a HMAC computation.
322 : */
323 : const char *
451 michael 324 UBC 0 : pg_hmac_error(pg_hmac_ctx *ctx)
325 : {
326 0 : if (ctx == NULL)
327 0 : return _("out of memory");
328 :
329 : /*
330 : * If a reason is provided, rely on it, else fallback to any error code
331 : * set.
332 : */
333 0 : if (ctx->errreason)
334 0 : return ctx->errreason;
335 :
336 0 : switch (ctx->error)
337 : {
338 0 : case PG_HMAC_ERROR_NONE:
339 0 : return _("success");
340 0 : case PG_HMAC_ERROR_DEST_LEN:
341 0 : return _("destination buffer too small");
342 0 : case PG_HMAC_ERROR_OPENSSL:
343 0 : return _("OpenSSL failure");
344 : }
345 :
346 0 : Assert(false); /* cannot be reached */
347 : return _("success");
348 : }
|