Skip to content

Commit 3a0cced

Browse files
committed
Improve error handling of cryptohash computations
The existing cryptohash facility was causing problems in some code paths related to MD5 (frontend and backend) that relied on the fact that the only type of error that could happen would be an OOM, as the MD5 implementation used in PostgreSQL ~13 (the in-core implementation is used when compiling with or without OpenSSL in those older versions), could fail only under this circumstance. The new cryptohash facilities can fail for reasons other than OOMs, like attempting MD5 when FIPS is enabled (upstream OpenSSL allows that up to 1.0.2, Fedora and Photon patch OpenSSL 1.1.1 to allow that), so this would cause incorrect reports to show up. This commit extends the cryptohash APIs so as callers of those routines can fetch more context when an error happens, by using a new routine called pg_cryptohash_error(). The error states are stored within each implementation's internal context data, so as it is possible to extend the logic depending on what's suited for an implementation. The default implementation requires few error states, but OpenSSL could report various issues depending on its internal state so more is needed in cryptohash_openssl.c, and the code is shaped so as we are always able to grab the necessary information. The core code is changed to adapt to the new error routine, painting more "const" across the call stack where the static errors are stored, particularly in authentication code paths on variables that provide log details. This way, any future changes would warn if attempting to free these strings. The MD5 authentication code was also a bit blurry about the handling of "logdetail" (LOG sent to the postmaster), so improve the comments related that, while on it. The origin of the problem is 87ae969, that introduced the centralized cryptohash facility. Extra changes are done for pgcrypto in v14 for the non-OpenSSL code path to cope with the improvements done by this commit. Reported-by: Michael Mühlbeyer Author: Michael Paquier Reviewed-by: Tom Lane Discussion: https://postgr.es/m/89B7F072-5BBE-4C92-903E-D83E865D9367@trivadis.com Backpatch-through: 14
1 parent 05cdda6 commit 3a0cced

File tree

19 files changed

+290
-84
lines changed

19 files changed

+290
-84
lines changed

contrib/passwordcheck/passwordcheck.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ check_password(const char *username,
7474
*
7575
* We only check for username = password.
7676
*/
77-
char *logdetail;
77+
const char *logdetail = NULL;
7878

7979
if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
8080
ereport(ERROR,

contrib/pgcrypto/internal-sha2.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ int_sha2_update(PX_MD *h, const uint8 *data, unsigned dlen)
101101
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
102102

103103
if (pg_cryptohash_update(ctx, data, dlen) < 0)
104-
elog(ERROR, "could not update %s context", "SHA2");
104+
elog(ERROR, "could not update %s context: %s", "SHA2",
105+
pg_cryptohash_error(ctx));
105106
}
106107

107108
static void
@@ -110,7 +111,8 @@ int_sha2_reset(PX_MD *h)
110111
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
111112

112113
if (pg_cryptohash_init(ctx) < 0)
113-
elog(ERROR, "could not initialize %s context", "SHA2");
114+
elog(ERROR, "could not initialize %s context: %s", "SHA2",
115+
pg_cryptohash_error(ctx));
114116
}
115117

116118
static void
@@ -119,7 +121,8 @@ int_sha2_finish(PX_MD *h, uint8 *dst)
119121
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
120122

121123
if (pg_cryptohash_final(ctx, dst, h->result_size(h)) < 0)
122-
elog(ERROR, "could not finalize %s context", "SHA2");
124+
elog(ERROR, "could not finalize %s context: %s", "SHA2",
125+
pg_cryptohash_error(ctx));
123126
}
124127

125128
static void

contrib/pgcrypto/internal.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ int_md5_update(PX_MD *h, const uint8 *data, unsigned dlen)
8989
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
9090

9191
if (pg_cryptohash_update(ctx, data, dlen) < 0)
92-
elog(ERROR, "could not update %s context", "MD5");
92+
elog(ERROR, "could not update %s context: %s", "MD5",
93+
pg_cryptohash_error(ctx));
9394
}
9495

9596
static void
@@ -98,7 +99,8 @@ int_md5_reset(PX_MD *h)
9899
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
99100

100101
if (pg_cryptohash_init(ctx) < 0)
101-
elog(ERROR, "could not initialize %s context", "MD5");
102+
elog(ERROR, "could not initialize %s context: %s", "MD5",
103+
pg_cryptohash_error(ctx));
102104
}
103105

104106
static void
@@ -107,7 +109,8 @@ int_md5_finish(PX_MD *h, uint8 *dst)
107109
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
108110

109111
if (pg_cryptohash_final(ctx, dst, h->result_size(h)) < 0)
110-
elog(ERROR, "could not finalize %s context", "MD5");
112+
elog(ERROR, "could not finalize %s context: %s", "MD5",
113+
pg_cryptohash_error(ctx));
111114
}
112115

113116
static void
@@ -139,7 +142,8 @@ int_sha1_update(PX_MD *h, const uint8 *data, unsigned dlen)
139142
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
140143

141144
if (pg_cryptohash_update(ctx, data, dlen) < 0)
142-
elog(ERROR, "could not update %s context", "SHA1");
145+
elog(ERROR, "could not update %s context: %s", "SHA1",
146+
pg_cryptohash_error(ctx));
143147
}
144148

145149
static void
@@ -148,7 +152,8 @@ int_sha1_reset(PX_MD *h)
148152
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
149153

150154
if (pg_cryptohash_init(ctx) < 0)
151-
elog(ERROR, "could not initialize %s context", "SHA1");
155+
elog(ERROR, "could not initialize %s context: %s", "SHA1",
156+
pg_cryptohash_error(ctx));
152157
}
153158

154159
static void
@@ -157,7 +162,8 @@ int_sha1_finish(PX_MD *h, uint8 *dst)
157162
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
158163

159164
if (pg_cryptohash_final(ctx, dst, h->result_size(h)) < 0)
160-
elog(ERROR, "could not finalize %s context", "SHA1");
165+
elog(ERROR, "could not finalize %s context: %s", "SHA1",
166+
pg_cryptohash_error(ctx));
161167
}
162168

163169
static void

contrib/uuid-ossp/uuid-ossp.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,14 +319,17 @@ uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
319319
pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_MD5);
320320

321321
if (pg_cryptohash_init(ctx) < 0)
322-
elog(ERROR, "could not initialize %s context", "MD5");
322+
elog(ERROR, "could not initialize %s context: %s", "MD5",
323+
pg_cryptohash_error(ctx));
323324
if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
324325
pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
325-
elog(ERROR, "could not update %s context", "MD5");
326+
elog(ERROR, "could not update %s context: %s", "MD5",
327+
pg_cryptohash_error(ctx));
326328
/* we assume sizeof MD5 result is 16, same as UUID size */
327329
if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
328330
sizeof(uu)) < 0)
329-
elog(ERROR, "could not finalize %s context", "MD5");
331+
elog(ERROR, "could not finalize %s context: %s", "MD5",
332+
pg_cryptohash_error(ctx));
330333
pg_cryptohash_free(ctx);
331334
}
332335
else
@@ -335,12 +338,15 @@ uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
335338
unsigned char sha1result[SHA1_DIGEST_LENGTH];
336339

337340
if (pg_cryptohash_init(ctx) < 0)
338-
elog(ERROR, "could not initialize %s context", "SHA1");
341+
elog(ERROR, "could not initialize %s context: %s", "SHA1",
342+
pg_cryptohash_error(ctx));
339343
if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
340344
pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
341-
elog(ERROR, "could not update %s context", "SHA1");
345+
elog(ERROR, "could not update %s context: %s", "SHA1",
346+
pg_cryptohash_error(ctx));
342347
if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
343-
elog(ERROR, "could not finalize %s context", "SHA1");
348+
elog(ERROR, "could not finalize %s context: %s", "SHA1",
349+
pg_cryptohash_error(ctx));
344350
pg_cryptohash_free(ctx);
345351

346352
memcpy(&uu, sha1result, sizeof(uu));

src/backend/commands/user.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
393393
if (password)
394394
{
395395
char *shadow_pass;
396-
char *logdetail;
396+
const char *logdetail = NULL;
397397

398398
/*
399399
* Don't allow an empty password. Libpq treats an empty password the
@@ -835,7 +835,7 @@ AlterRole(AlterRoleStmt *stmt)
835835
if (password)
836836
{
837837
char *shadow_pass;
838-
char *logdetail;
838+
const char *logdetail = NULL;
839839

840840
/* Like in CREATE USER, don't allow an empty password. */
841841
if (password[0] == '\0' ||

src/backend/libpq/auth-scram.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ pg_be_scram_init(Port *port,
327327
*/
328328
int
329329
pg_be_scram_exchange(void *opaq, const char *input, int inputlen,
330-
char **output, int *outputlen, char **logdetail)
330+
char **output, int *outputlen, const char **logdetail)
331331
{
332332
scram_state *state = (scram_state *) opaq;
333333
int result;

src/backend/libpq/auth.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
*/
4848
static void sendAuthRequest(Port *port, AuthRequest areq, const char *extradata,
4949
int extralen);
50-
static void auth_failed(Port *port, int status, char *logdetail);
50+
static void auth_failed(Port *port, int status, const char *logdetail);
5151
static char *recv_password_packet(Port *port);
5252
static void set_authn_id(Port *port, const char *id);
5353

@@ -56,11 +56,11 @@ static void set_authn_id(Port *port, const char *id);
5656
* Password-based authentication methods (password, md5, and scram-sha-256)
5757
*----------------------------------------------------------------
5858
*/
59-
static int CheckPasswordAuth(Port *port, char **logdetail);
60-
static int CheckPWChallengeAuth(Port *port, char **logdetail);
59+
static int CheckPasswordAuth(Port *port, const char **logdetail);
60+
static int CheckPWChallengeAuth(Port *port, const char **logdetail);
6161

62-
static int CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail);
63-
static int CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail);
62+
static int CheckMD5Auth(Port *port, char *shadow_pass, const char **logdetail);
63+
static int CheckSCRAMAuth(Port *port, char *shadow_pass, const char **logdetail);
6464

6565

6666
/*----------------------------------------------------------------
@@ -258,7 +258,7 @@ ClientAuthentication_hook_type ClientAuthentication_hook = NULL;
258258
* particular, if logdetail isn't NULL, we send that string to the log.
259259
*/
260260
static void
261-
auth_failed(Port *port, int status, char *logdetail)
261+
auth_failed(Port *port, int status, const char *logdetail)
262262
{
263263
const char *errstr;
264264
char *cdetail;
@@ -394,7 +394,7 @@ void
394394
ClientAuthentication(Port *port)
395395
{
396396
int status = STATUS_ERROR;
397-
char *logdetail = NULL;
397+
const char *logdetail = NULL;
398398

399399
/*
400400
* Get the authentication method to use for this frontend/database
@@ -780,7 +780,7 @@ recv_password_packet(Port *port)
780780
* Plaintext password authentication.
781781
*/
782782
static int
783-
CheckPasswordAuth(Port *port, char **logdetail)
783+
CheckPasswordAuth(Port *port, const char **logdetail)
784784
{
785785
char *passwd;
786786
int result;
@@ -815,7 +815,7 @@ CheckPasswordAuth(Port *port, char **logdetail)
815815
* MD5 and SCRAM authentication.
816816
*/
817817
static int
818-
CheckPWChallengeAuth(Port *port, char **logdetail)
818+
CheckPWChallengeAuth(Port *port, const char **logdetail)
819819
{
820820
int auth_result;
821821
char *shadow_pass;
@@ -875,7 +875,7 @@ CheckPWChallengeAuth(Port *port, char **logdetail)
875875
}
876876

877877
static int
878-
CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail)
878+
CheckMD5Auth(Port *port, char *shadow_pass, const char **logdetail)
879879
{
880880
char md5Salt[4]; /* Password salt */
881881
char *passwd;
@@ -912,7 +912,7 @@ CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail)
912912
}
913913

914914
static int
915-
CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
915+
CheckSCRAMAuth(Port *port, char *shadow_pass, const char **logdetail)
916916
{
917917
StringInfoData sasl_mechs;
918918
int mtype;
@@ -3240,6 +3240,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
32403240
md5trailer = packet->vector;
32413241
for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
32423242
{
3243+
const char *errstr = NULL;
3244+
32433245
memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
32443246

32453247
/*
@@ -3248,10 +3250,12 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
32483250
*/
32493251
md5trailer = encryptedpassword + i;
32503252

3251-
if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
3253+
if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH,
3254+
encryptedpassword + i, &errstr))
32523255
{
32533256
ereport(LOG,
3254-
(errmsg("could not perform MD5 encryption of password")));
3257+
(errmsg("could not perform MD5 encryption of password: %s",
3258+
errstr)));
32553259
pfree(cryptvector);
32563260
pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
32573261
return STATUS_ERROR;
@@ -3336,6 +3340,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
33363340
struct timeval timeout;
33373341
struct timeval now;
33383342
int64 timeoutval;
3343+
const char *errstr = NULL;
33393344

33403345
gettimeofday(&now, NULL);
33413346
timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec);
@@ -3454,10 +3459,11 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
34543459

34553460
if (!pg_md5_binary(cryptvector,
34563461
packetlength + strlen(secret),
3457-
encryptedpassword))
3462+
encryptedpassword, &errstr))
34583463
{
34593464
ereport(LOG,
3460-
(errmsg("could not perform MD5 encryption of received packet")));
3465+
(errmsg("could not perform MD5 encryption of received packet: %s",
3466+
errstr)));
34613467
pfree(cryptvector);
34623468
continue;
34633469
}

src/backend/libpq/crypt.c

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
* sent to the client, to avoid giving away user information!
3535
*/
3636
char *
37-
get_role_password(const char *role, char **logdetail)
37+
get_role_password(const char *role, const char **logdetail)
3838
{
3939
TimestampTz vuntil = 0;
4040
HeapTuple roleTup;
@@ -116,6 +116,7 @@ encrypt_password(PasswordType target_type, const char *role,
116116
{
117117
PasswordType guessed_type = get_password_type(password);
118118
char *encrypted_password;
119+
const char *errstr = NULL;
119120

120121
if (guessed_type != PASSWORD_TYPE_PLAINTEXT)
121122
{
@@ -132,8 +133,8 @@ encrypt_password(PasswordType target_type, const char *role,
132133
encrypted_password = palloc(MD5_PASSWD_LEN + 1);
133134

134135
if (!pg_md5_encrypt(password, role, strlen(role),
135-
encrypted_password))
136-
elog(ERROR, "password encryption failed");
136+
encrypted_password, &errstr))
137+
elog(ERROR, "password encryption failed: %s", errstr);
137138
return encrypted_password;
138139

139140
case PASSWORD_TYPE_SCRAM_SHA_256:
@@ -159,17 +160,18 @@ encrypt_password(PasswordType target_type, const char *role,
159160
* 'client_pass' is the response given by the remote user to the MD5 challenge.
160161
* 'md5_salt' is the salt used in the MD5 authentication challenge.
161162
*
162-
* In the error case, optionally store a palloc'd string at *logdetail
163-
* that will be sent to the postmaster log (but not the client).
163+
* In the error case, save a string at *logdetail that will be sent to the
164+
* postmaster log (but not the client).
164165
*/
165166
int
166167
md5_crypt_verify(const char *role, const char *shadow_pass,
167168
const char *client_pass,
168169
const char *md5_salt, int md5_salt_len,
169-
char **logdetail)
170+
const char **logdetail)
170171
{
171172
int retval;
172173
char crypt_pwd[MD5_PASSWD_LEN + 1];
174+
const char *errstr = NULL;
173175

174176
Assert(md5_salt_len > 0);
175177

@@ -183,16 +185,13 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
183185

184186
/*
185187
* Compute the correct answer for the MD5 challenge.
186-
*
187-
* We do not bother setting logdetail for any pg_md5_encrypt failure
188-
* below: the only possible error is out-of-memory, which is unlikely, and
189-
* if it did happen adding a psprintf call would only make things worse.
190188
*/
191189
/* stored password already encrypted, only do salt */
192190
if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
193191
md5_salt, md5_salt_len,
194-
crypt_pwd))
192+
crypt_pwd, &errstr))
195193
{
194+
*logdetail = errstr;
196195
return STATUS_ERROR;
197196
}
198197

@@ -215,15 +214,16 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
215214
* pg_authid.rolpassword.
216215
* 'client_pass' is the password given by the remote user.
217216
*
218-
* In the error case, optionally store a palloc'd string at *logdetail
219-
* that will be sent to the postmaster log (but not the client).
217+
* In the error case, store a string at *logdetail that will be sent to the
218+
* postmaster log (but not the client).
220219
*/
221220
int
222221
plain_crypt_verify(const char *role, const char *shadow_pass,
223222
const char *client_pass,
224-
char **logdetail)
223+
const char **logdetail)
225224
{
226225
char crypt_client_pass[MD5_PASSWD_LEN + 1];
226+
const char *errstr = NULL;
227227

228228
/*
229229
* Client sent password in plaintext. If we have an MD5 hash stored, hash
@@ -251,14 +251,10 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
251251
if (!pg_md5_encrypt(client_pass,
252252
role,
253253
strlen(role),
254-
crypt_client_pass))
254+
crypt_client_pass,
255+
&errstr))
255256
{
256-
/*
257-
* We do not bother setting logdetail for pg_md5_encrypt
258-
* failure: the only possible error is out-of-memory, which is
259-
* unlikely, and if it did happen adding a psprintf call would
260-
* only make things worse.
261-
*/
257+
*logdetail = errstr;
262258
return STATUS_ERROR;
263259
}
264260
if (strcmp(crypt_client_pass, shadow_pass) == 0)

0 commit comments

Comments
 (0)