Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b69aba7

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 9ef2c65 commit b69aba7

File tree

18 files changed

+275
-77
lines changed

18 files changed

+275
-77
lines changed

contrib/passwordcheck/passwordcheck.c

+1-1
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/uuid-ossp/uuid-ossp.c

+12-6
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

+2-2
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
355355
if (password)
356356
{
357357
char *shadow_pass;
358-
char *logdetail;
358+
const char *logdetail = NULL;
359359

360360
/*
361361
* Don't allow an empty password. Libpq treats an empty password the
@@ -775,7 +775,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
775775
if (password)
776776
{
777777
char *shadow_pass;
778-
char *logdetail;
778+
const char *logdetail = NULL;
779779

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

src/backend/libpq/auth-sasl.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
*/
5151
int
5252
CheckSASLAuth(const pg_be_sasl_mech *mech, Port *port, char *shadow_pass,
53-
char **logdetail)
53+
const char **logdetail)
5454
{
5555
StringInfoData sasl_mechs;
5656
int mtype;

src/backend/libpq/auth-scram.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ static void scram_get_mechanisms(Port *port, StringInfo buf);
111111
static void *scram_init(Port *port, const char *selected_mech,
112112
const char *shadow_pass);
113113
static int scram_exchange(void *opaq, const char *input, int inputlen,
114-
char **output, int *outputlen, char **logdetail);
114+
char **output, int *outputlen,
115+
const char **logdetail);
115116

116117
/* Mechanism declaration */
117118
const pg_be_sasl_mech pg_be_scram_mech = {
@@ -335,7 +336,7 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
335336
*/
336337
static int
337338
scram_exchange(void *opaq, const char *input, int inputlen,
338-
char **output, int *outputlen, char **logdetail)
339+
char **output, int *outputlen, const char **logdetail)
339340
{
340341
scram_state *state = (scram_state *) opaq;
341342
int result;

src/backend/libpq/auth.c

+20-13
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* Global authentication functions
4646
*----------------------------------------------------------------
4747
*/
48-
static void auth_failed(Port *port, int status, char *logdetail);
48+
static void auth_failed(Port *port, int status, const char *logdetail);
4949
static char *recv_password_packet(Port *port);
5050
static void set_authn_id(Port *port, const char *id);
5151

@@ -54,10 +54,11 @@ static void set_authn_id(Port *port, const char *id);
5454
* Password-based authentication methods (password, md5, and scram-sha-256)
5555
*----------------------------------------------------------------
5656
*/
57-
static int CheckPasswordAuth(Port *port, char **logdetail);
58-
static int CheckPWChallengeAuth(Port *port, char **logdetail);
57+
static int CheckPasswordAuth(Port *port, const char **logdetail);
58+
static int CheckPWChallengeAuth(Port *port, const char **logdetail);
5959

60-
static int CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail);
60+
static int CheckMD5Auth(Port *port, char *shadow_pass,
61+
const char **logdetail);
6162

6263

6364
/*----------------------------------------------------------------
@@ -247,7 +248,7 @@ ClientAuthentication_hook_type ClientAuthentication_hook = NULL;
247248
* particular, if logdetail isn't NULL, we send that string to the log.
248249
*/
249250
static void
250-
auth_failed(Port *port, int status, char *logdetail)
251+
auth_failed(Port *port, int status, const char *logdetail)
251252
{
252253
const char *errstr;
253254
char *cdetail;
@@ -383,7 +384,7 @@ void
383384
ClientAuthentication(Port *port)
384385
{
385386
int status = STATUS_ERROR;
386-
char *logdetail = NULL;
387+
const char *logdetail = NULL;
387388

388389
/*
389390
* Get the authentication method to use for this frontend/database
@@ -769,7 +770,7 @@ recv_password_packet(Port *port)
769770
* Plaintext password authentication.
770771
*/
771772
static int
772-
CheckPasswordAuth(Port *port, char **logdetail)
773+
CheckPasswordAuth(Port *port, const char **logdetail)
773774
{
774775
char *passwd;
775776
int result;
@@ -804,7 +805,7 @@ CheckPasswordAuth(Port *port, char **logdetail)
804805
* MD5 and SCRAM authentication.
805806
*/
806807
static int
807-
CheckPWChallengeAuth(Port *port, char **logdetail)
808+
CheckPWChallengeAuth(Port *port, const char **logdetail)
808809
{
809810
int auth_result;
810811
char *shadow_pass;
@@ -866,7 +867,7 @@ CheckPWChallengeAuth(Port *port, char **logdetail)
866867
}
867868

868869
static int
869-
CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail)
870+
CheckMD5Auth(Port *port, char *shadow_pass, const char **logdetail)
870871
{
871872
char md5Salt[4]; /* Password salt */
872873
char *passwd;
@@ -3085,6 +3086,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
30853086
md5trailer = packet->vector;
30863087
for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
30873088
{
3089+
const char *errstr = NULL;
3090+
30883091
memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
30893092

30903093
/*
@@ -3093,10 +3096,12 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
30933096
*/
30943097
md5trailer = encryptedpassword + i;
30953098

3096-
if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
3099+
if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH,
3100+
encryptedpassword + i, &errstr))
30973101
{
30983102
ereport(LOG,
3099-
(errmsg("could not perform MD5 encryption of password")));
3103+
(errmsg("could not perform MD5 encryption of password: %s",
3104+
errstr)));
31003105
pfree(cryptvector);
31013106
pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
31023107
return STATUS_ERROR;
@@ -3181,6 +3186,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
31813186
struct timeval timeout;
31823187
struct timeval now;
31833188
int64 timeoutval;
3189+
const char *errstr = NULL;
31843190

31853191
gettimeofday(&now, NULL);
31863192
timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec);
@@ -3299,10 +3305,11 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
32993305

33003306
if (!pg_md5_binary(cryptvector,
33013307
packetlength + strlen(secret),
3302-
encryptedpassword))
3308+
encryptedpassword, &errstr))
33033309
{
33043310
ereport(LOG,
3305-
(errmsg("could not perform MD5 encryption of received packet")));
3311+
(errmsg("could not perform MD5 encryption of received packet: %s",
3312+
errstr)));
33063313
pfree(cryptvector);
33073314
continue;
33083315
}

src/backend/libpq/crypt.c

+17-21
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)

src/backend/replication/backup_manifest.c

+7-4
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ InitializeBackupManifest(backup_manifest_info *manifest,
6868
manifest->buffile = BufFileCreateTemp(false);
6969
manifest->manifest_ctx = pg_cryptohash_create(PG_SHA256);
7070
if (pg_cryptohash_init(manifest->manifest_ctx) < 0)
71-
elog(ERROR, "failed to initialize checksum of backup manifest");
71+
elog(ERROR, "failed to initialize checksum of backup manifest: %s",
72+
pg_cryptohash_error(manifest->manifest_ctx));
7273
}
7374

7475
manifest->manifest_size = UINT64CONST(0);
@@ -311,7 +312,7 @@ AddWALInfoToBackupManifest(backup_manifest_info *manifest, XLogRecPtr startptr,
311312
* Finalize the backup manifest, and send it to the client.
312313
*/
313314
void
314-
SendBackupManifest(backup_manifest_info *manifest, bbsink *sink)
315+
SendBackupManifest(backup_manifest_info *manifest, bbsink * sink)
315316
{
316317
uint8 checksumbuf[PG_SHA256_DIGEST_LENGTH];
317318
char checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH];
@@ -334,7 +335,8 @@ SendBackupManifest(backup_manifest_info *manifest, bbsink *sink)
334335
manifest->still_checksumming = false;
335336
if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf,
336337
sizeof(checksumbuf)) < 0)
337-
elog(ERROR, "failed to finalize checksum of backup manifest");
338+
elog(ERROR, "failed to finalize checksum of backup manifest: %s",
339+
pg_cryptohash_error(manifest->manifest_ctx));
338340
AppendStringToManifest(manifest, "\"Manifest-Checksum\": \"");
339341

340342
hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf);
@@ -391,7 +393,8 @@ AppendStringToManifest(backup_manifest_info *manifest, char *s)
391393
if (manifest->still_checksumming)
392394
{
393395
if (pg_cryptohash_update(manifest->manifest_ctx, (uint8 *) s, len) < 0)
394-
elog(ERROR, "failed to update checksum of backup manifest");
396+
elog(ERROR, "failed to update checksum of backup manifest: %s",
397+
pg_cryptohash_error(manifest->manifest_ctx));
395398
}
396399
BufFileWrite(manifest->buffile, s, len);
397400
manifest->manifest_size += len;

0 commit comments

Comments
 (0)