PostgreSQL Source Code  git master
cryptohash_openssl.c
Go to the documentation of this file.
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 {
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 {
68  const char *errreason;
69 
70  EVP_MD_CTX *evpctx;
71 
72 #ifndef FRONTEND
74 #endif
75 };
76 
77 static const char *
78 SSLerrmessage(unsigned long ecode)
79 {
80  if (ecode == 0)
81  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  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  */
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
108 #endif
109 
110  ctx = ALLOC(sizeof(pg_cryptohash_ctx));
111  if (ctx == NULL)
112  return NULL;
113  memset(ctx, 0, sizeof(pg_cryptohash_ctx));
114  ctx->type = type;
116  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  */
123  ERR_clear_error();
124  ctx->evpctx = EVP_MD_CTX_create();
125 
126  if (ctx->evpctx == NULL)
127  {
128  explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
129  FREE(ctx);
130 #ifndef FRONTEND
131  ereport(ERROR,
132  (errcode(ERRCODE_OUT_OF_MEMORY),
133  errmsg("out of memory")));
134 #else
135  return NULL;
136 #endif
137  }
138 
139 #ifndef FRONTEND
142  PointerGetDatum(ctx));
143 #endif
144 
145  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
155 {
156  int status = 0;
157 
158  if (ctx == NULL)
159  return -1;
160 
161  switch (ctx->type)
162  {
163  case PG_MD5:
164  status = EVP_DigestInit_ex(ctx->evpctx, EVP_md5(), NULL);
165  break;
166  case PG_SHA1:
167  status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha1(), NULL);
168  break;
169  case PG_SHA224:
170  status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha224(), NULL);
171  break;
172  case PG_SHA256:
173  status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha256(), NULL);
174  break;
175  case PG_SHA384:
176  status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha384(), NULL);
177  break;
178  case PG_SHA512:
179  status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha512(), NULL);
180  break;
181  }
182 
183  /* OpenSSL internals return 1 on success, 0 on failure */
184  if (status <= 0)
185  {
186  ctx->errreason = SSLerrmessage(ERR_get_error());
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  */
194  ERR_clear_error();
195  return -1;
196  }
197  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
207 {
208  int status = 0;
209 
210  if (ctx == NULL)
211  return -1;
212 
213  status = EVP_DigestUpdate(ctx->evpctx, data, len);
214 
215  /* OpenSSL internals return 1 on success, 0 on failure */
216  if (status <= 0)
217  {
218  ctx->errreason = SSLerrmessage(ERR_get_error());
220  return -1;
221  }
222  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
232 {
233  int status = 0;
234 
235  if (ctx == NULL)
236  return -1;
237 
238  switch (ctx->type)
239  {
240  case PG_MD5:
241  if (len < MD5_DIGEST_LENGTH)
242  {
244  return -1;
245  }
246  break;
247  case PG_SHA1:
248  if (len < SHA1_DIGEST_LENGTH)
249  {
251  return -1;
252  }
253  break;
254  case PG_SHA224:
256  {
258  return -1;
259  }
260  break;
261  case PG_SHA256:
263  {
265  return -1;
266  }
267  break;
268  case PG_SHA384:
270  {
272  return -1;
273  }
274  break;
275  case PG_SHA512:
277  {
279  return -1;
280  }
281  break;
282  }
283 
284  status = EVP_DigestFinal_ex(ctx->evpctx, dest, 0);
285 
286  /* OpenSSL internals return 1 on success, 0 on failure */
287  if (status <= 0)
288  {
289  ctx->errreason = SSLerrmessage(ERR_get_error());
291  return -1;
292  }
293  return 0;
294 }
295 
296 /*
297  * pg_cryptohash_free
298  *
299  * Free a hash context.
300  */
301 void
303 {
304  if (ctx == NULL)
305  return;
306 
307  EVP_MD_CTX_destroy(ctx->evpctx);
308 
309 #ifndef FRONTEND
311  PointerGetDatum(ctx));
312 #endif
313 
314  explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
315  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 *
326 {
327  /*
328  * This implementation would never fail because of an out-of-memory error,
329  * except when creating the context.
330  */
331  if (ctx == NULL)
332  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  if (ctx->errreason)
339  return ctx->errreason;
340 
341  switch (ctx->error)
342  {
344  return _("success");
346  return _("destination buffer too small");
348  return _("OpenSSL failure");
349  }
350 
351  Assert(false); /* cannot be reached */
352  return _("success");
353 }
unsigned char uint8
Definition: c.h:493
pg_cryptohash_errno
Definition: cryptohash.c:45
pg_cryptohash_type
Definition: cryptohash.h:20
@ PG_SHA512
Definition: cryptohash.h:26
@ PG_SHA224
Definition: cryptohash.h:23
@ PG_SHA384
Definition: cryptohash.h:25
@ PG_SHA1
Definition: cryptohash.h:22
@ PG_SHA256
Definition: cryptohash.h:24
@ PG_MD5
Definition: cryptohash.h:21
#define FREE(ptr)
int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
int pg_cryptohash_init(pg_cryptohash_ctx *ctx)
static const char * SSLerrmessage(unsigned long ecode)
pg_cryptohash_errno
@ PG_CRYPTOHASH_ERROR_DEST_LEN
@ PG_CRYPTOHASH_ERROR_NONE
@ PG_CRYPTOHASH_ERROR_OPENSSL
#define ALLOC(size)
void pg_cryptohash_free(pg_cryptohash_ctx *ctx)
pg_cryptohash_ctx * pg_cryptohash_create(pg_cryptohash_type type)
int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
const char * pg_cryptohash_error(pg_cryptohash_ctx *ctx)
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define _(x)
Definition: elog.c:91
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
Assert(fmt[strlen(fmt) - 1] !='\n')
#define MD5_DIGEST_LENGTH
Definition: md5.h:20
const void size_t len
const void * data
void explicit_bzero(void *buf, size_t len)
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
ResourceOwner CurrentResourceOwner
Definition: resowner.c:147
void ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
Definition: resowner.c:1486
void ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
Definition: resowner.c:1475
void ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
Definition: resowner.c:1495
#define SHA1_DIGEST_LENGTH
Definition: sha1.h:17
#define PG_SHA256_DIGEST_LENGTH
Definition: sha2.h:23
#define PG_SHA384_DIGEST_LENGTH
Definition: sha2.h:26
#define PG_SHA512_DIGEST_LENGTH
Definition: sha2.h:29
#define PG_SHA224_DIGEST_LENGTH
Definition: sha2.h:20
pg_cryptohash_errno error
Definition: cryptohash.c:54
ResourceOwner resowner
pg_cryptohash_type type
Definition: cryptohash.c:53
const char * errreason
const char * type