Age Owner TLA 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-2023, 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 : #include "utils/resowner_private.h"
35 : #endif
36 :
37 : /*
38 : * In the backend, use an allocation in TopMemoryContext to count for
39 : * resowner cleanup handling. In the frontend, use malloc to be able
40 : * to return a failure status back to the caller.
41 : */
42 : #ifndef FRONTEND
43 : #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
44 : #define FREE(ptr) pfree(ptr)
45 : #else
46 : #define ALLOC(size) malloc(size)
47 : #define FREE(ptr) free(ptr)
48 : #endif
49 :
50 : /* Set of error states */
51 : typedef enum pg_cryptohash_errno
52 : {
53 : PG_CRYPTOHASH_ERROR_NONE = 0,
54 : PG_CRYPTOHASH_ERROR_DEST_LEN,
55 : PG_CRYPTOHASH_ERROR_OPENSSL
56 : } pg_cryptohash_errno;
57 :
58 : /*
59 : * Internal pg_cryptohash_ctx structure.
60 : *
61 : * This tracks the resource owner associated to each EVP context data
62 : * for the backend.
63 : */
64 : struct pg_cryptohash_ctx
65 : {
66 : pg_cryptohash_type type;
67 : pg_cryptohash_errno error;
68 : const char *errreason;
69 :
70 : EVP_MD_CTX *evpctx;
71 :
72 : #ifndef FRONTEND
73 : ResourceOwner resowner;
74 : #endif
75 : };
76 :
77 : static const char *
453 michael 78 UBC 0 : SSLerrmessage(unsigned long ecode)
79 : {
80 0 : if (ecode == 0)
81 0 : return NULL;
82 :
83 : /*
84 : * This may return NULL, but we would fall back to a default error path if
85 : * that were the case.
86 : */
87 0 : return ERR_reason_error_string(ecode);
88 : }
89 :
90 : /*
91 : * pg_cryptohash_create
92 : *
93 : * Allocate a hash context. Returns NULL on failure for an OOM. The
94 : * backend issues an error, without returning.
95 : */
96 : pg_cryptohash_ctx *
858 michael 97 CBC 503216 : pg_cryptohash_create(pg_cryptohash_type type)
98 : {
99 : pg_cryptohash_ctx *ctx;
100 :
101 : /*
102 : * Make sure that the resource owner has space to remember this reference.
103 : * This can error out with "out of memory", so do this before any other
104 : * allocation to avoid leaking.
105 : */
106 : #ifndef FRONTEND
822 107 499247 : ResourceOwnerEnlargeCryptoHash(CurrentResourceOwner);
108 : #endif
109 :
858 110 503216 : ctx = ALLOC(sizeof(pg_cryptohash_ctx));
111 503216 : if (ctx == NULL)
858 michael 112 UBC 0 : return NULL;
822 michael 113 CBC 503216 : memset(ctx, 0, sizeof(pg_cryptohash_ctx));
856 114 503216 : ctx->type = type;
453 115 503216 : ctx->error = PG_CRYPTOHASH_ERROR_NONE;
116 503216 : ctx->errreason = NULL;
117 :
118 : /*
119 : * Initialization takes care of assigning the correct type for OpenSSL.
120 : * Also ensure that there aren't any unconsumed errors in the queue from
121 : * previous runs.
122 : */
338 dgustafsson 123 503216 : ERR_clear_error();
822 michael 124 503216 : ctx->evpctx = EVP_MD_CTX_create();
125 :
126 503216 : if (ctx->evpctx == NULL)
127 : {
858 michael 128 UBC 0 : explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
822 129 0 : FREE(ctx);
130 : #ifndef FRONTEND
856 131 0 : ereport(ERROR,
132 : (errcode(ERRCODE_OUT_OF_MEMORY),
133 : errmsg("out of memory")));
134 : #else
858 135 0 : return NULL;
136 : #endif
137 : }
138 :
139 : #ifndef FRONTEND
822 michael 140 CBC 499247 : ctx->resowner = CurrentResourceOwner;
856 141 499247 : ResourceOwnerRememberCryptoHash(CurrentResourceOwner,
142 : PointerGetDatum(ctx));
143 : #endif
144 :
858 145 503216 : return ctx;
146 : }
147 :
148 : /*
149 : * pg_cryptohash_init
150 : *
151 : * Initialize a hash context. Returns 0 on success, and -1 on failure.
152 : */
153 : int
154 503216 : pg_cryptohash_init(pg_cryptohash_ctx *ctx)
155 : {
156 503216 : int status = 0;
157 :
158 503216 : if (ctx == NULL)
822 michael 159 UBC 0 : return -1;
160 :
858 michael 161 CBC 503216 : switch (ctx->type)
162 : {
850 163 395998 : case PG_MD5:
822 164 395998 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_md5(), NULL);
850 165 395998 : break;
806 166 1 : case PG_SHA1:
167 1 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha1(), NULL);
168 1 : break;
858 169 1937 : case PG_SHA224:
822 170 1937 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha224(), NULL);
858 171 1937 : break;
172 101406 : case PG_SHA256:
822 173 101406 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha256(), NULL);
858 174 101406 : break;
175 1937 : case PG_SHA384:
822 176 1937 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha384(), NULL);
858 177 1937 : break;
178 1937 : case PG_SHA512:
822 179 1937 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha512(), NULL);
858 180 1937 : break;
181 : }
182 :
183 : /* OpenSSL internals return 1 on success, 0 on failure */
184 503216 : if (status <= 0)
185 : {
453 michael 186 UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
187 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
188 :
189 : /*
190 : * The OpenSSL error queue should normally be empty since we've
191 : * consumed an error, but cipher initialization can in FIPS-enabled
192 : * OpenSSL builds generate two errors so clear the queue here as well.
193 : */
338 dgustafsson 194 0 : ERR_clear_error();
858 michael 195 0 : return -1;
196 : }
858 michael 197 CBC 503216 : return 0;
198 : }
199 :
200 : /*
201 : * pg_cryptohash_update
202 : *
203 : * Update a hash context. Returns 0 on success, and -1 on failure.
204 : */
205 : int
206 642374 : pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
207 : {
208 642374 : int status = 0;
209 :
210 642374 : if (ctx == NULL)
822 michael 211 UBC 0 : return -1;
212 :
822 michael 213 CBC 642374 : status = EVP_DigestUpdate(ctx->evpctx, data, len);
214 :
215 : /* OpenSSL internals return 1 on success, 0 on failure */
858 216 642374 : if (status <= 0)
217 : {
453 michael 218 UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
219 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
858 220 0 : return -1;
221 : }
858 michael 222 CBC 642374 : return 0;
223 : }
224 :
225 : /*
226 : * pg_cryptohash_final
227 : *
228 : * Finalize a hash context. Returns 0 on success, and -1 on failure.
229 : */
230 : int
783 231 503210 : pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
232 : {
858 233 503210 : int status = 0;
234 :
235 503210 : if (ctx == NULL)
822 michael 236 UBC 0 : return -1;
237 :
783 michael 238 CBC 503210 : switch (ctx->type)
239 : {
240 395998 : case PG_MD5:
241 395998 : if (len < MD5_DIGEST_LENGTH)
242 : {
453 michael 243 UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
783 244 0 : return -1;
245 : }
783 michael 246 CBC 395998 : break;
247 1 : case PG_SHA1:
248 1 : if (len < SHA1_DIGEST_LENGTH)
249 : {
453 michael 250 UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
783 251 0 : return -1;
252 : }
783 michael 253 CBC 1 : break;
254 1937 : case PG_SHA224:
255 1937 : if (len < PG_SHA224_DIGEST_LENGTH)
256 : {
453 michael 257 UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
783 258 0 : return -1;
259 : }
783 michael 260 CBC 1937 : break;
261 101400 : case PG_SHA256:
262 101400 : if (len < PG_SHA256_DIGEST_LENGTH)
263 : {
453 michael 264 UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
783 265 0 : return -1;
266 : }
783 michael 267 CBC 101400 : break;
268 1937 : case PG_SHA384:
269 1937 : if (len < PG_SHA384_DIGEST_LENGTH)
270 : {
453 michael 271 UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
783 272 0 : return -1;
273 : }
783 michael 274 CBC 1937 : break;
275 1937 : case PG_SHA512:
276 1937 : if (len < PG_SHA512_DIGEST_LENGTH)
277 : {
453 michael 278 UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
783 279 0 : return -1;
280 : }
783 michael 281 CBC 1937 : break;
282 : }
283 :
822 284 503210 : status = EVP_DigestFinal_ex(ctx->evpctx, dest, 0);
285 :
286 : /* OpenSSL internals return 1 on success, 0 on failure */
858 287 503210 : if (status <= 0)
288 : {
453 michael 289 UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
290 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
858 291 0 : return -1;
292 : }
858 michael 293 CBC 503210 : return 0;
294 : }
295 :
296 : /*
297 : * pg_cryptohash_free
298 : *
299 : * Free a hash context.
300 : */
301 : void
302 503210 : pg_cryptohash_free(pg_cryptohash_ctx *ctx)
303 : {
304 503210 : if (ctx == NULL)
305 1 : return;
306 :
822 307 503209 : EVP_MD_CTX_destroy(ctx->evpctx);
308 :
309 : #ifndef FRONTEND
310 499242 : ResourceOwnerForgetCryptoHash(ctx->resowner,
311 : PointerGetDatum(ctx));
312 : #endif
313 :
858 314 503209 : explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
315 503209 : FREE(ctx);
316 : }
317 :
318 : /*
319 : * pg_cryptohash_error
320 : *
321 : * Returns a static string providing details about an error that
322 : * happened during a computation.
323 : */
324 : const char *
453 michael 325 UBC 0 : pg_cryptohash_error(pg_cryptohash_ctx *ctx)
326 : {
327 : /*
328 : * This implementation would never fail because of an out-of-memory error,
329 : * except when creating the context.
330 : */
331 0 : if (ctx == NULL)
332 0 : return _("out of memory");
333 :
334 : /*
335 : * If a reason is provided, rely on it, else fallback to any error code
336 : * set.
337 : */
338 0 : if (ctx->errreason)
339 0 : return ctx->errreason;
340 :
341 0 : switch (ctx->error)
342 : {
343 0 : case PG_CRYPTOHASH_ERROR_NONE:
344 0 : return _("success");
345 0 : case PG_CRYPTOHASH_ERROR_DEST_LEN:
346 0 : return _("destination buffer too small");
347 0 : case PG_CRYPTOHASH_ERROR_OPENSSL:
348 0 : return _("OpenSSL failure");
349 : }
350 :
351 0 : Assert(false); /* cannot be reached */
352 : return _("success");
353 : }
|