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-2025, 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. In frontend, use malloc to be able to return
39 * a failure status back to the caller.
40 */
41#ifndef FRONTEND
42#define USE_RESOWNER_FOR_HMAC
43#define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
44#define FREE(ptr) pfree(ptr)
45#else /* FRONTEND */
46#define ALLOC(size) malloc(size)
47#define FREE(ptr) free(ptr)
48#endif /* FRONTEND */
49
50/* Set of error states */
51typedef enum pg_hmac_errno
52{
57
58/* Internal pg_hmac_ctx structure */
59struct pg_hmac_ctx
60{
61 HMAC_CTX *hmacctx;
64 const char *errreason;
65
66#ifdef USE_RESOWNER_FOR_HMAC
68#endif
69};
70
71/* ResourceOwner callbacks to hold HMAC contexts */
72#ifdef USE_RESOWNER_FOR_HMAC
73static void ResOwnerReleaseHMAC(Datum res);
74
76{
77 .name = "OpenSSL HMAC context",
78 .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
79 .release_priority = RELEASE_PRIO_HMAC_CONTEXTS,
80 .ReleaseResource = ResOwnerReleaseHMAC,
81 .DebugPrint = NULL /* the default message is fine */
82};
83
84/* Convenience wrappers over ResourceOwnerRemember/Forget */
85static inline void
87{
89}
90static inline void
92{
94}
95#endif
96
97static const char *
98SSLerrmessage(unsigned long ecode)
99{
100 if (ecode == 0)
101 return NULL;
102
103 /*
104 * This may return NULL, but we would fall back to a default error path if
105 * that were the case.
106 */
107 return ERR_reason_error_string(ecode);
108}
109
110/*
111 * pg_hmac_create
112 *
113 * Allocate a hash context. Returns NULL on failure for an OOM. The
114 * backend issues an error, without returning.
115 */
118{
119 pg_hmac_ctx *ctx;
120
121 ctx = ALLOC(sizeof(pg_hmac_ctx));
122 if (ctx == NULL)
123 return NULL;
124 memset(ctx, 0, sizeof(pg_hmac_ctx));
125
126 ctx->type = type;
128 ctx->errreason = NULL;
129
130
131 /*
132 * Initialization takes care of assigning the correct type for OpenSSL.
133 * Also ensure that there aren't any unconsumed errors in the queue from
134 * previous runs.
135 */
136 ERR_clear_error();
137
138#ifdef USE_RESOWNER_FOR_HMAC
140#endif
141
142 ctx->hmacctx = HMAC_CTX_new();
143
144 if (ctx->hmacctx == NULL)
145 {
146 explicit_bzero(ctx, sizeof(pg_hmac_ctx));
147 FREE(ctx);
148#ifndef FRONTEND
150 (errcode(ERRCODE_OUT_OF_MEMORY),
151 errmsg("out of memory")));
152#endif
153 return NULL;
154 }
155
156
157#ifdef USE_RESOWNER_FOR_HMAC
160#endif
161
162 return ctx;
163}
164
165/*
166 * pg_hmac_init
167 *
168 * Initialize a HMAC context. Returns 0 on success, -1 on failure.
169 */
170int
171pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
172{
173 int status = 0;
174
175 if (ctx == NULL)
176 return -1;
177
178 switch (ctx->type)
179 {
180 case PG_MD5:
181 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_md5(), NULL);
182 break;
183 case PG_SHA1:
184 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha1(), NULL);
185 break;
186 case PG_SHA224:
187 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha224(), NULL);
188 break;
189 case PG_SHA256:
190 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha256(), NULL);
191 break;
192 case PG_SHA384:
193 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha384(), NULL);
194 break;
195 case PG_SHA512:
196 status = HMAC_Init_ex(ctx->hmacctx, key, len, EVP_sha512(), NULL);
197 break;
198 }
199
200 /* OpenSSL internals return 1 on success, 0 on failure */
201 if (status <= 0)
202 {
203 ctx->errreason = SSLerrmessage(ERR_get_error());
205 return -1;
206 }
207
208 return 0;
209}
210
211/*
212 * pg_hmac_update
213 *
214 * Update a HMAC context. Returns 0 on success, -1 on failure.
215 */
216int
218{
219 int status = 0;
220
221 if (ctx == NULL)
222 return -1;
223
224 status = HMAC_Update(ctx->hmacctx, data, len);
225
226 /* OpenSSL internals return 1 on success, 0 on failure */
227 if (status <= 0)
228 {
229 ctx->errreason = SSLerrmessage(ERR_get_error());
231 return -1;
232 }
233 return 0;
234}
235
236/*
237 * pg_hmac_final
238 *
239 * Finalize a HMAC context. Returns 0 on success, -1 on failure.
240 */
241int
243{
244 int status = 0;
245 uint32 outlen;
246
247 if (ctx == NULL)
248 return -1;
249
250 switch (ctx->type)
251 {
252 case PG_MD5:
254 {
256 return -1;
257 }
258 break;
259 case PG_SHA1:
261 {
263 return -1;
264 }
265 break;
266 case PG_SHA224:
268 {
270 return -1;
271 }
272 break;
273 case PG_SHA256:
275 {
277 return -1;
278 }
279 break;
280 case PG_SHA384:
282 {
284 return -1;
285 }
286 break;
287 case PG_SHA512:
289 {
291 return -1;
292 }
293 break;
294 }
295
296 status = HMAC_Final(ctx->hmacctx, dest, &outlen);
297
298 /* OpenSSL internals return 1 on success, 0 on failure */
299 if (status <= 0)
300 {
301 ctx->errreason = SSLerrmessage(ERR_get_error());
303 return -1;
304 }
305 return 0;
306}
307
308/*
309 * pg_hmac_free
310 *
311 * Free a HMAC context.
312 */
313void
315{
316 if (ctx == NULL)
317 return;
318
319 HMAC_CTX_free(ctx->hmacctx);
320#ifdef USE_RESOWNER_FOR_HMAC
321 if (ctx->resowner)
323#endif
324
325 explicit_bzero(ctx, sizeof(pg_hmac_ctx));
326 FREE(ctx);
327}
328
329/*
330 * pg_hmac_error
331 *
332 * Returns a static string providing details about an error that happened
333 * during a HMAC computation.
334 */
335const char *
337{
338 if (ctx == NULL)
339 return _("out of memory");
340
341 /*
342 * If a reason is provided, rely on it, else fallback to any error code
343 * set.
344 */
345 if (ctx->errreason)
346 return ctx->errreason;
347
348 switch (ctx->error)
349 {
351 return _("success");
353 return _("destination buffer too small");
355 return _("OpenSSL failure");
356 }
357
358 Assert(false); /* cannot be reached */
359 return _("success");
360}
361
362/* ResourceOwner callbacks */
363
364#ifdef USE_RESOWNER_FOR_HMAC
365static void
367{
369
370 ctx->resowner = NULL;
371 pg_hmac_free(ctx);
372}
373#endif
uint8_t uint8
Definition: c.h:486
#define Assert(condition)
Definition: c.h:815
uint32_t uint32
Definition: c.h:488
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:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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
static void ResOwnerReleaseHMAC(Datum res)
Definition: hmac_openssl.c:366
#define FREE(ptr)
Definition: hmac_openssl.c:44
pg_hmac_ctx * pg_hmac_create(pg_cryptohash_type type)
Definition: hmac_openssl.c:117
static const ResourceOwnerDesc hmac_resowner_desc
Definition: hmac_openssl.c:75
static void ResourceOwnerForgetHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
Definition: hmac_openssl.c:91
static const char * SSLerrmessage(unsigned long ecode)
Definition: hmac_openssl.c:98
static void ResourceOwnerRememberHMAC(ResourceOwner owner, pg_hmac_ctx *ctx)
Definition: hmac_openssl.c:86
void pg_hmac_free(pg_hmac_ctx *ctx)
Definition: hmac_openssl.c:314
const char * pg_hmac_error(pg_hmac_ctx *ctx)
Definition: hmac_openssl.c:336
int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
Definition: hmac_openssl.c:217
int pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
Definition: hmac_openssl.c:171
#define ALLOC(size)
Definition: hmac_openssl.c:43
pg_hmac_errno
Definition: hmac_openssl.c:52
@ PG_HMAC_ERROR_OPENSSL
Definition: hmac_openssl.c:55
@ PG_HMAC_ERROR_DEST_LEN
Definition: hmac_openssl.c:54
@ PG_HMAC_ERROR_NONE
Definition: hmac_openssl.c:53
int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
Definition: hmac_openssl.c:242
#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:327
uintptr_t Datum
Definition: postgres.h:69
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
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
#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
ResourceOwner resowner
Definition: hmac_openssl.c:67
HMAC_CTX * hmacctx
Definition: hmac_openssl.c:61
pg_cryptohash_type type
Definition: hmac.c:53
pg_hmac_errno error
Definition: hmac.c:54
const char * type