PostgreSQL Source Code  git master
hmac_openssl.c
Go to the documentation of this file.
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-2024, 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 #endif
35 
36 /*
37  * In backend, use an allocation in TopMemoryContext to count for resowner
38  * cleanup handling if necessary. For versions of OpenSSL where HMAC_CTX is
39  * known, just use palloc(). In frontend, use malloc to be able to return
40  * a failure status back to the caller.
41  */
42 #ifndef FRONTEND
43 #ifdef HAVE_HMAC_CTX_NEW
44 #define USE_RESOWNER_FOR_HMAC
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 {
62 
63 /* Internal pg_hmac_ctx structure */
64 struct pg_hmac_ctx
65 {
66  HMAC_CTX *hmacctx;
69  const char *errreason;
70 
71 #ifdef USE_RESOWNER_FOR_HMAC
72  ResourceOwner resowner;
73 #endif
74 };
75 
76 /* ResourceOwner callbacks to hold HMAC contexts */
77 #ifdef USE_RESOWNER_FOR_HMAC
78 static void ResOwnerReleaseHMAC(Datum res);
79 
80 static const ResourceOwnerDesc hmac_resowner_desc =
81 {
82  .name = "OpenSSL HMAC context",
83  .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
84  .release_priority = RELEASE_PRIO_HMAC_CONTEXTS,
85  .ReleaseResource = ResOwnerReleaseHMAC,
86  .DebugPrint = NULL /* the default message is fine */
87 };
88 
89 /* Convenience wrappers over ResourceOwnerRemember/Forget */
90 static inline void
92 {
93  ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
94 }
95 static inline void
97 {
98  ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_desc);
99 }
100 #endif
101 
102 static const char *
103 SSLerrmessage(unsigned long ecode)
104 {
105  if (ecode == 0)
106  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  return ERR_reason_error_string(ecode);
113 }
114 
115 /*
116  * pg_hmac_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_hmac_ctx *
123 {
124  pg_hmac_ctx *ctx;
125 
126  ctx = ALLOC(sizeof(pg_hmac_ctx));
127  if (ctx == NULL)
128  return NULL;
129  memset(ctx, 0, sizeof(pg_hmac_ctx));
130 
131  ctx->type = type;
132  ctx->error = PG_HMAC_ERROR_NONE;
133  ctx->errreason = NULL;
134 
135 
136  /*
137  * Initialization takes care of assigning the correct type for OpenSSL.
138  * Also ensure that there aren't any unconsumed errors in the queue from
139  * previous runs.
140  */
141  ERR_clear_error();
142 
143 #ifdef USE_RESOWNER_FOR_HMAC
145 #endif
146 
147 #ifdef HAVE_HMAC_CTX_NEW
148  ctx->hmacctx = HMAC_CTX_new();
149 #else
150  ctx->hmacctx = ALLOC(sizeof(HMAC_CTX));
151 #endif
152 
153  if (ctx->hmacctx == NULL)
154  {
155  explicit_bzero(ctx, sizeof(pg_hmac_ctx));
156  FREE(ctx);
157 #ifndef FRONTEND
158  ereport(ERROR,
159  (errcode(ERRCODE_OUT_OF_MEMORY),
160  errmsg("out of memory")));
161 #endif
162  return NULL;
163  }
164 
165 #ifndef HAVE_HMAC_CTX_NEW
166  memset(ctx->hmacctx, 0, sizeof(HMAC_CTX));
167 #endif
168 
169 #ifdef USE_RESOWNER_FOR_HMAC
170  ctx->resowner = CurrentResourceOwner;
172 #endif
173 
174  return ctx;
175 }
176 
177 /*
178  * pg_hmac_init
179  *
180  * Initialize a HMAC context. Returns 0 on success, -1 on failure.
181  */
182 int
183 pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
184 {
185  int status = 0;
186 
187  if (ctx == NULL)
188  return -1;
189 
190  switch (ctx->type)
191  {
192  case PG_MD5:
193  status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
194  break;
195  case PG_SHA1:
196  status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
197  break;
198  case PG_SHA224:
199  status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
200  break;
201  case PG_SHA256:
202  status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
203  break;
204  case PG_SHA384:
205  status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
206  break;
207  case PG_SHA512:
208  status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
209  break;
210  }
211 
212  /* OpenSSL internals return 1 on success, 0 on failure */
213  if (status <= 0)
214  {
215  ctx->errreason = SSLerrmessage(ERR_get_error());
217  return -1;
218  }
219 
220  return 0;
221 }
222 
223 /*
224  * pg_hmac_update
225  *
226  * Update a HMAC context. Returns 0 on success, -1 on failure.
227  */
228 int
229 pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
230 {
231  int status = 0;
232 
233  if (ctx == NULL)
234  return -1;
235 
236  status = HMAC_Update(ctx->hmacctx, data, len);
237 
238  /* OpenSSL internals return 1 on success, 0 on failure */
239  if (status <= 0)
240  {
241  ctx->errreason = SSLerrmessage(ERR_get_error());
243  return -1;
244  }
245  return 0;
246 }
247 
248 /*
249  * pg_hmac_final
250  *
251  * Finalize a HMAC context. Returns 0 on success, -1 on failure.
252  */
253 int
255 {
256  int status = 0;
257  uint32 outlen;
258 
259  if (ctx == NULL)
260  return -1;
261 
262  switch (ctx->type)
263  {
264  case PG_MD5:
265  if (len < MD5_DIGEST_LENGTH)
266  {
268  return -1;
269  }
270  break;
271  case PG_SHA1:
272  if (len < SHA1_DIGEST_LENGTH)
273  {
275  return -1;
276  }
277  break;
278  case PG_SHA224:
280  {
282  return -1;
283  }
284  break;
285  case PG_SHA256:
287  {
289  return -1;
290  }
291  break;
292  case PG_SHA384:
294  {
296  return -1;
297  }
298  break;
299  case PG_SHA512:
301  {
303  return -1;
304  }
305  break;
306  }
307 
308  status = HMAC_Final(ctx->hmacctx, dest, &outlen);
309 
310  /* OpenSSL internals return 1 on success, 0 on failure */
311  if (status <= 0)
312  {
313  ctx->errreason = SSLerrmessage(ERR_get_error());
315  return -1;
316  }
317  return 0;
318 }
319 
320 /*
321  * pg_hmac_free
322  *
323  * Free a HMAC context.
324  */
325 void
327 {
328  if (ctx == NULL)
329  return;
330 
331 #ifdef HAVE_HMAC_CTX_FREE
332  HMAC_CTX_free(ctx->hmacctx);
333 #else
334  explicit_bzero(ctx->hmacctx, sizeof(HMAC_CTX));
335  FREE(ctx->hmacctx);
336 #endif
337 
338 #ifdef USE_RESOWNER_FOR_HMAC
339  if (ctx->resowner)
340  ResourceOwnerForgetHMAC(ctx->resowner, ctx);
341 #endif
342 
343  explicit_bzero(ctx, sizeof(pg_hmac_ctx));
344  FREE(ctx);
345 }
346 
347 /*
348  * pg_hmac_error
349  *
350  * Returns a static string providing details about an error that happened
351  * during a HMAC computation.
352  */
353 const char *
355 {
356  if (ctx == NULL)
357  return _("out of memory");
358 
359  /*
360  * If a reason is provided, rely on it, else fallback to any error code
361  * set.
362  */
363  if (ctx->errreason)
364  return ctx->errreason;
365 
366  switch (ctx->error)
367  {
368  case PG_HMAC_ERROR_NONE:
369  return _("success");
371  return _("destination buffer too small");
373  return _("OpenSSL failure");
374  }
375 
376  Assert(false); /* cannot be reached */
377  return _("success");
378 }
379 
380 /* ResourceOwner callbacks */
381 
382 #ifdef USE_RESOWNER_FOR_HMAC
383 static void
384 ResOwnerReleaseHMAC(Datum res)
385 {
387 
388  ctx->resowner = NULL;
389  pg_hmac_free(ctx);
390 }
391 #endif
unsigned int uint32
Definition: c.h:506
#define Assert(condition)
Definition: c.h:858
unsigned char uint8
Definition: c.h:504
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
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
pg_hmac_errno
Definition: hmac.c:43
pg_hmac_ctx * pg_hmac_create(pg_cryptohash_type type)
Definition: hmac_openssl.c:122
#define FREE(ptr)
Definition: hmac_openssl.c:49
const char * pg_hmac_error(pg_hmac_ctx *ctx)
Definition: hmac_openssl.c:354
static const char * SSLerrmessage(unsigned long ecode)
Definition: hmac_openssl.c:103
void pg_hmac_free(pg_hmac_ctx *ctx)
Definition: hmac_openssl.c:326
int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
Definition: hmac_openssl.c:229
int pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
Definition: hmac_openssl.c:183
#define ALLOC(size)
Definition: hmac_openssl.c:47
pg_hmac_errno
Definition: hmac_openssl.c:57
@ PG_HMAC_ERROR_OPENSSL
Definition: hmac_openssl.c:60
@ PG_HMAC_ERROR_DEST_LEN
Definition: hmac_openssl.c:59
@ PG_HMAC_ERROR_NONE
Definition: hmac_openssl.c:58
int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
Definition: hmac_openssl.c:254
#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
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:554
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:514
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition: resowner.c:442
#define RELEASE_PRIO_HMAC_CONTEXTS
Definition: resowner.h:68
@ RESOURCE_RELEASE_BEFORE_LOCKS
Definition: resowner.h:54
void ResourceOwnerRememberHMAC(ResourceOwner owner, Datum handle)
void ResourceOwnerForgetHMAC(ResourceOwner owner, Datum handle)
#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
const char * name
Definition: resowner.h:93
const char * errreason
Definition: hmac.c:55
HMAC_CTX * hmacctx
Definition: hmac_openssl.c:66
pg_cryptohash_type type
Definition: hmac.c:53
pg_hmac_errno error
Definition: hmac.c:54
const char * type