Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * cryptohash_openssl.c
4 : * Set of wrapper routines on top of OpenSSL to support cryptographic
5 : * hash functions.
6 : *
7 : * This should only be used if code is compiled with OpenSSL support.
8 : *
9 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
10 : * Portions Copyright (c) 1994, Regents of the University of California
11 : *
12 : * IDENTIFICATION
13 : * src/common/cryptohash_openssl.c
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 :
18 : #ifndef FRONTEND
19 : #include "postgres.h"
20 : #else
21 : #include "postgres_fe.h"
22 : #endif
23 :
24 : #include <openssl/err.h>
25 : #include <openssl/evp.h>
26 :
27 : #include "common/cryptohash.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 the backend, use an allocation in TopMemoryContext to count for
38 : * resowner cleanup handling. In the frontend, use malloc to be able
39 : * to return a failure status back to the caller.
40 : */
41 : #ifndef FRONTEND
42 : #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
43 : #define FREE(ptr) pfree(ptr)
44 : #else
45 : #define ALLOC(size) malloc(size)
46 : #define FREE(ptr) free(ptr)
47 : #endif
48 :
49 : /* Set of error states */
50 : typedef enum pg_cryptohash_errno
51 : {
52 : PG_CRYPTOHASH_ERROR_NONE = 0,
53 : PG_CRYPTOHASH_ERROR_DEST_LEN,
54 : PG_CRYPTOHASH_ERROR_OPENSSL,
55 : } pg_cryptohash_errno;
56 :
57 : /*
58 : * Internal pg_cryptohash_ctx structure.
59 : *
60 : * This tracks the resource owner associated to each EVP context data
61 : * for the backend.
62 : */
63 : struct pg_cryptohash_ctx
64 : {
65 : pg_cryptohash_type type;
66 : pg_cryptohash_errno error;
67 : const char *errreason;
68 :
69 : EVP_MD_CTX *evpctx;
70 :
71 : #ifndef FRONTEND
72 : ResourceOwner resowner;
73 : #endif
74 : };
75 :
76 : /* ResourceOwner callbacks to hold cryptohash contexts */
77 : #ifndef FRONTEND
78 : static void ResOwnerReleaseCryptoHash(Datum res);
79 :
80 : static const ResourceOwnerDesc cryptohash_resowner_desc =
81 : {
82 : .name = "OpenSSL cryptohash context",
83 : .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
84 : .release_priority = RELEASE_PRIO_CRYPTOHASH_CONTEXTS,
85 : .ReleaseResource = ResOwnerReleaseCryptoHash,
86 : .DebugPrint = NULL /* the default message is fine */
87 : };
88 :
89 : /* Convenience wrappers over ResourceOwnerRemember/Forget */
90 : static inline void
91 861078 : ResourceOwnerRememberCryptoHash(ResourceOwner owner, pg_cryptohash_ctx *ctx)
92 : {
93 861078 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &cryptohash_resowner_desc);
94 861078 : }
95 : static inline void
96 861056 : ResourceOwnerForgetCryptoHash(ResourceOwner owner, pg_cryptohash_ctx *ctx)
97 : {
98 861056 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &cryptohash_resowner_desc);
99 861056 : }
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_cryptohash_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_cryptohash_ctx *
122 880788 : pg_cryptohash_create(pg_cryptohash_type type)
123 : {
124 : pg_cryptohash_ctx *ctx;
125 :
126 : /*
127 : * Make sure that the resource owner has space to remember this reference.
128 : * This can error out with "out of memory", so do this before any other
129 : * allocation to avoid leaking.
130 : */
131 : #ifndef FRONTEND
132 861078 : ResourceOwnerEnlarge(CurrentResourceOwner);
133 : #endif
134 :
135 880788 : ctx = ALLOC(sizeof(pg_cryptohash_ctx));
136 880788 : if (ctx == NULL)
137 0 : return NULL;
138 880788 : memset(ctx, 0, sizeof(pg_cryptohash_ctx));
139 880788 : ctx->type = type;
140 880788 : ctx->error = PG_CRYPTOHASH_ERROR_NONE;
141 880788 : ctx->errreason = NULL;
142 :
143 : /*
144 : * Initialization takes care of assigning the correct type for OpenSSL.
145 : * Also ensure that there aren't any unconsumed errors in the queue from
146 : * previous runs.
147 : */
148 880788 : ERR_clear_error();
149 880788 : ctx->evpctx = EVP_MD_CTX_create();
150 :
151 880788 : if (ctx->evpctx == NULL)
152 : {
153 0 : explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
154 0 : FREE(ctx);
155 : #ifndef FRONTEND
156 0 : ereport(ERROR,
157 : (errcode(ERRCODE_OUT_OF_MEMORY),
158 : errmsg("out of memory")));
159 : #else
160 0 : return NULL;
161 : #endif
162 : }
163 :
164 : #ifndef FRONTEND
165 861078 : ctx->resowner = CurrentResourceOwner;
166 861078 : ResourceOwnerRememberCryptoHash(CurrentResourceOwner, ctx);
167 : #endif
168 :
169 880788 : return ctx;
170 : }
171 :
172 : /*
173 : * pg_cryptohash_init
174 : *
175 : * Initialize a hash context. Returns 0 on success, and -1 on failure.
176 : */
177 : int
178 880788 : pg_cryptohash_init(pg_cryptohash_ctx *ctx)
179 : {
180 880788 : int status = 0;
181 :
182 880788 : if (ctx == NULL)
183 0 : return -1;
184 :
185 880788 : switch (ctx->type)
186 : {
187 142 : case PG_MD5:
188 142 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_md5(), NULL);
189 142 : break;
190 0 : case PG_SHA1:
191 0 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha1(), NULL);
192 0 : break;
193 11650 : case PG_SHA224:
194 11650 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha224(), NULL);
195 11650 : break;
196 853452 : case PG_SHA256:
197 853452 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha256(), NULL);
198 853452 : break;
199 7772 : case PG_SHA384:
200 7772 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha384(), NULL);
201 7772 : break;
202 7772 : case PG_SHA512:
203 7772 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha512(), NULL);
204 7772 : break;
205 : }
206 :
207 : /* OpenSSL internals return 1 on success, 0 on failure */
208 880788 : if (status <= 0)
209 : {
210 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
211 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
212 :
213 : /*
214 : * The OpenSSL error queue should normally be empty since we've
215 : * consumed an error, but cipher initialization can in FIPS-enabled
216 : * OpenSSL builds generate two errors so clear the queue here as well.
217 : */
218 0 : ERR_clear_error();
219 0 : return -1;
220 : }
221 880788 : return 0;
222 : }
223 :
224 : /*
225 : * pg_cryptohash_update
226 : *
227 : * Update a hash context. Returns 0 on success, and -1 on failure.
228 : */
229 : int
230 1186086 : pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
231 : {
232 1186086 : int status = 0;
233 :
234 1186086 : if (ctx == NULL)
235 0 : return -1;
236 :
237 1186086 : status = EVP_DigestUpdate(ctx->evpctx, data, len);
238 :
239 : /* OpenSSL internals return 1 on success, 0 on failure */
240 1186086 : if (status <= 0)
241 : {
242 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
243 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
244 0 : return -1;
245 : }
246 1186086 : return 0;
247 : }
248 :
249 : /*
250 : * pg_cryptohash_final
251 : *
252 : * Finalize a hash context. Returns 0 on success, and -1 on failure.
253 : */
254 : int
255 880770 : pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
256 : {
257 880770 : int status = 0;
258 :
259 880770 : if (ctx == NULL)
260 0 : return -1;
261 :
262 880770 : switch (ctx->type)
263 : {
264 142 : case PG_MD5:
265 142 : if (len < MD5_DIGEST_LENGTH)
266 : {
267 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
268 0 : return -1;
269 : }
270 142 : break;
271 0 : case PG_SHA1:
272 0 : if (len < SHA1_DIGEST_LENGTH)
273 : {
274 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
275 0 : return -1;
276 : }
277 0 : break;
278 11650 : case PG_SHA224:
279 11650 : if (len < PG_SHA224_DIGEST_LENGTH)
280 : {
281 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
282 0 : return -1;
283 : }
284 11650 : break;
285 853434 : case PG_SHA256:
286 853434 : if (len < PG_SHA256_DIGEST_LENGTH)
287 : {
288 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
289 0 : return -1;
290 : }
291 853434 : break;
292 7772 : case PG_SHA384:
293 7772 : if (len < PG_SHA384_DIGEST_LENGTH)
294 : {
295 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
296 0 : return -1;
297 : }
298 7772 : break;
299 7772 : case PG_SHA512:
300 7772 : if (len < PG_SHA512_DIGEST_LENGTH)
301 : {
302 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
303 0 : return -1;
304 : }
305 7772 : break;
306 : }
307 :
308 880770 : status = EVP_DigestFinal_ex(ctx->evpctx, dest, 0);
309 :
310 : /* OpenSSL internals return 1 on success, 0 on failure */
311 880770 : if (status <= 0)
312 : {
313 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
314 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
315 0 : return -1;
316 : }
317 880770 : return 0;
318 : }
319 :
320 : /*
321 : * pg_cryptohash_free
322 : *
323 : * Free a hash context.
324 : */
325 : void
326 880782 : pg_cryptohash_free(pg_cryptohash_ctx *ctx)
327 : {
328 880782 : if (ctx == NULL)
329 2 : return;
330 :
331 880780 : EVP_MD_CTX_destroy(ctx->evpctx);
332 :
333 : #ifndef FRONTEND
334 861078 : if (ctx->resowner)
335 861056 : ResourceOwnerForgetCryptoHash(ctx->resowner, ctx);
336 : #endif
337 :
338 880780 : explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
339 880780 : FREE(ctx);
340 : }
341 :
342 : /*
343 : * pg_cryptohash_error
344 : *
345 : * Returns a static string providing details about an error that
346 : * happened during a computation.
347 : */
348 : const char *
349 0 : pg_cryptohash_error(pg_cryptohash_ctx *ctx)
350 : {
351 : /*
352 : * This implementation would never fail because of an out-of-memory error,
353 : * except when creating the context.
354 : */
355 0 : if (ctx == NULL)
356 0 : return _("out of memory");
357 :
358 : /*
359 : * If a reason is provided, rely on it, else fallback to any error code
360 : * set.
361 : */
362 0 : if (ctx->errreason)
363 0 : return ctx->errreason;
364 :
365 0 : switch (ctx->error)
366 : {
367 0 : case PG_CRYPTOHASH_ERROR_NONE:
368 0 : return _("success");
369 0 : case PG_CRYPTOHASH_ERROR_DEST_LEN:
370 0 : return _("destination buffer too small");
371 0 : case PG_CRYPTOHASH_ERROR_OPENSSL:
372 0 : return _("OpenSSL failure");
373 : }
374 :
375 : Assert(false); /* cannot be reached */
376 0 : return _("success");
377 : }
378 :
379 : /* ResourceOwner callbacks */
380 :
381 : #ifndef FRONTEND
382 : static void
383 22 : ResOwnerReleaseCryptoHash(Datum res)
384 : {
385 22 : pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) DatumGetPointer(res);
386 :
387 22 : ctx->resowner = NULL;
388 22 : pg_cryptohash_free(ctx);
389 22 : }
390 : #endif
|