Age Owner Branch data 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-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
158 heikki.linnakangas@i 91 :GNC 430322 : ResourceOwnerRememberCryptoHash(ResourceOwner owner, pg_cryptohash_ctx *ctx)
92 : : {
93 : 430322 : ResourceOwnerRemember(owner, PointerGetDatum(ctx), &cryptohash_resowner_desc);
94 : 430322 : }
95 : : static inline void
96 : 430312 : ResourceOwnerForgetCryptoHash(ResourceOwner owner, pg_cryptohash_ctx *ctx)
97 : : {
98 : 430312 : ResourceOwnerForget(owner, PointerGetDatum(ctx), &cryptohash_resowner_desc);
99 : 430312 : }
100 : : #endif
101 : :
102 : : static const char *
824 michael@paquier.xyz 103 :UBC 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 *
1229 michael@paquier.xyz 122 :CBC 436260 : 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
158 heikki.linnakangas@i 132 :GNC 430322 : ResourceOwnerEnlarge(CurrentResourceOwner);
133 : : #endif
134 : :
1229 michael@paquier.xyz 135 :CBC 436260 : ctx = ALLOC(sizeof(pg_cryptohash_ctx));
136 [ - + ]: 436260 : if (ctx == NULL)
1229 michael@paquier.xyz 137 :UBC 0 : return NULL;
1193 michael@paquier.xyz 138 :CBC 436260 : memset(ctx, 0, sizeof(pg_cryptohash_ctx));
1227 139 : 436260 : ctx->type = type;
824 140 : 436260 : ctx->error = PG_CRYPTOHASH_ERROR_NONE;
141 : 436260 : 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 : : */
709 dgustafsson@postgres 148 : 436260 : ERR_clear_error();
1193 michael@paquier.xyz 149 : 436260 : ctx->evpctx = EVP_MD_CTX_create();
150 : :
151 [ - + ]: 436260 : if (ctx->evpctx == NULL)
152 : : {
1229 michael@paquier.xyz 153 :UBC 0 : explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
1193 154 : 0 : FREE(ctx);
155 : : #ifndef FRONTEND
1227 156 [ # # ]: 0 : ereport(ERROR,
157 : : (errcode(ERRCODE_OUT_OF_MEMORY),
158 : : errmsg("out of memory")));
159 : : #else
1229 160 : 0 : return NULL;
161 : : #endif
162 : : }
163 : :
164 : : #ifndef FRONTEND
1193 michael@paquier.xyz 165 :CBC 430322 : ctx->resowner = CurrentResourceOwner;
158 heikki.linnakangas@i 166 :GNC 430322 : ResourceOwnerRememberCryptoHash(CurrentResourceOwner, ctx);
167 : : #endif
168 : :
1229 michael@paquier.xyz 169 :CBC 436260 : 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 : 436260 : pg_cryptohash_init(pg_cryptohash_ctx *ctx)
179 : : {
180 : 436260 : int status = 0;
181 : :
182 [ - + ]: 436260 : if (ctx == NULL)
1193 michael@paquier.xyz 183 :UBC 0 : return -1;
184 : :
1229 michael@paquier.xyz 185 [ + + + + :CBC 436260 : switch (ctx->type)
+ + - ]
186 : : {
1221 187 : 4552 : case PG_MD5:
1193 188 : 4552 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_md5(), NULL);
1221 189 : 4552 : break;
1177 190 : 1 : case PG_SHA1:
191 : 1 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha1(), NULL);
192 : 1 : break;
1229 193 : 3868 : case PG_SHA224:
1193 194 : 3868 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha224(), NULL);
1229 195 : 3868 : break;
196 : 423965 : case PG_SHA256:
1193 197 : 423965 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha256(), NULL);
1229 198 : 423965 : break;
199 : 1937 : case PG_SHA384:
1193 200 : 1937 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha384(), NULL);
1229 201 : 1937 : break;
202 : 1937 : case PG_SHA512:
1193 203 : 1937 : status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha512(), NULL);
1229 204 : 1937 : break;
205 : : }
206 : :
207 : : /* OpenSSL internals return 1 on success, 0 on failure */
208 [ - + ]: 436260 : if (status <= 0)
209 : : {
824 michael@paquier.xyz 210 :UBC 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 : : */
709 dgustafsson@postgres 218 : 0 : ERR_clear_error();
1229 michael@paquier.xyz 219 : 0 : return -1;
220 : : }
1229 michael@paquier.xyz 221 :CBC 436260 : 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 : 582340 : pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
231 : : {
232 : 582340 : int status = 0;
233 : :
234 [ - + ]: 582340 : if (ctx == NULL)
1193 michael@paquier.xyz 235 :UBC 0 : return -1;
236 : :
1193 michael@paquier.xyz 237 :CBC 582340 : status = EVP_DigestUpdate(ctx->evpctx, data, len);
238 : :
239 : : /* OpenSSL internals return 1 on success, 0 on failure */
1229 240 [ - + ]: 582340 : if (status <= 0)
241 : : {
824 michael@paquier.xyz 242 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
243 : 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
1229 244 : 0 : return -1;
245 : : }
1229 michael@paquier.xyz 246 :CBC 582340 : 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
1154 255 : 436253 : pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
256 : : {
1229 257 : 436253 : int status = 0;
258 : :
259 [ - + ]: 436253 : if (ctx == NULL)
1193 michael@paquier.xyz 260 :UBC 0 : return -1;
261 : :
1154 michael@paquier.xyz 262 [ + + + + :CBC 436253 : switch (ctx->type)
+ + - ]
263 : : {
264 : 4552 : case PG_MD5:
265 [ - + ]: 4552 : if (len < MD5_DIGEST_LENGTH)
266 : : {
824 michael@paquier.xyz 267 :UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
1154 268 : 0 : return -1;
269 : : }
1154 michael@paquier.xyz 270 :CBC 4552 : break;
271 : 1 : case PG_SHA1:
272 [ - + ]: 1 : if (len < SHA1_DIGEST_LENGTH)
273 : : {
824 michael@paquier.xyz 274 :UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
1154 275 : 0 : return -1;
276 : : }
1154 michael@paquier.xyz 277 :CBC 1 : break;
278 : 3868 : case PG_SHA224:
279 [ - + ]: 3868 : if (len < PG_SHA224_DIGEST_LENGTH)
280 : : {
824 michael@paquier.xyz 281 :UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
1154 282 : 0 : return -1;
283 : : }
1154 michael@paquier.xyz 284 :CBC 3868 : break;
285 : 423958 : case PG_SHA256:
286 [ - + ]: 423958 : if (len < PG_SHA256_DIGEST_LENGTH)
287 : : {
824 michael@paquier.xyz 288 :UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
1154 289 : 0 : return -1;
290 : : }
1154 michael@paquier.xyz 291 :CBC 423958 : break;
292 : 1937 : case PG_SHA384:
293 [ - + ]: 1937 : if (len < PG_SHA384_DIGEST_LENGTH)
294 : : {
824 michael@paquier.xyz 295 :UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
1154 296 : 0 : return -1;
297 : : }
1154 michael@paquier.xyz 298 :CBC 1937 : break;
299 : 1937 : case PG_SHA512:
300 [ - + ]: 1937 : if (len < PG_SHA512_DIGEST_LENGTH)
301 : : {
824 michael@paquier.xyz 302 :UBC 0 : ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
1154 303 : 0 : return -1;
304 : : }
1154 michael@paquier.xyz 305 :CBC 1937 : break;
306 : : }
307 : :
1193 308 : 436253 : status = EVP_DigestFinal_ex(ctx->evpctx, dest, 0);
309 : :
310 : : /* OpenSSL internals return 1 on success, 0 on failure */
1229 311 [ - + ]: 436253 : if (status <= 0)
312 : : {
824 michael@paquier.xyz 313 :UBC 0 : ctx->errreason = SSLerrmessage(ERR_get_error());
314 : 0 : ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
1229 315 : 0 : return -1;
316 : : }
1229 michael@paquier.xyz 317 :CBC 436253 : return 0;
318 : : }
319 : :
320 : : /*
321 : : * pg_cryptohash_free
322 : : *
323 : : * Free a hash context.
324 : : */
325 : : void
326 : 436254 : pg_cryptohash_free(pg_cryptohash_ctx *ctx)
327 : : {
328 [ + + ]: 436254 : if (ctx == NULL)
329 : 1 : return;
330 : :
1193 331 : 436253 : EVP_MD_CTX_destroy(ctx->evpctx);
332 : :
333 : : #ifndef FRONTEND
158 heikki.linnakangas@i 334 [ + + ]:GNC 430317 : if (ctx->resowner)
335 : 430312 : ResourceOwnerForgetCryptoHash(ctx->resowner, ctx);
336 : : #endif
337 : :
1229 michael@paquier.xyz 338 :CBC 436253 : explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
339 : 436253 : 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 *
824 michael@paquier.xyz 349 :UBC 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 : 0 : Assert(false); /* cannot be reached */
376 : : return _("success");
377 : : }
378 : :
379 : : /* ResourceOwner callbacks */
380 : :
381 : : #ifndef FRONTEND
382 : : static void
158 heikki.linnakangas@i 383 :GNC 5 : ResOwnerReleaseCryptoHash(Datum res)
384 : : {
385 : 5 : pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) DatumGetPointer(res);
386 : :
387 : 5 : ctx->resowner = NULL;
388 : 5 : pg_cryptohash_free(ctx);
389 : 5 : }
390 : : #endif
|