Search Postgresql Archives

Re: md5 issues Postgres14 on OL7

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jan 05, 2022 at 04:09:12PM +0900, Michael Paquier wrote:
> In order to make things portable with 14 in cryptohash.c, we don't
> have any need to change the existing cryptohash APIs.  We could just
> store in each implementation context a location to a static string,
> and add a new routine to extract it if there is an error, defaulting
> to OOM.

I have been looking at that, and finished with the attached.  It is
close to the end of the day, so this needs an extra lookup, but I have
finished by using the idea of an extra routine, called
pg_cryptohash_error(), able to grab the error saved in the private
contexts, so as any callers from the backend or the frontend can feed
on that.  This way, it is possible to make the difference between
several class of errors: OOMs, a too short destination buffer, OpenSSL
internal error, etc.

There are a couple of things worth noting here:
- Two code paths of src/backend/libpq/crypt.c rely on the result of
pg_md5_encrypt() to always be an OOM, so as this skips one
psnprintf().  This has been let as-is in the backend for now, but we
don't pfree() the *logdetail strings passed across the various layers,
so we could just pass down the cryptohash error as-is..  We'd better
mention that logdetail may not be palloc'd all the time, once we do
that.  libpq is able to use that properly.
- The routines of md5_common.c need to pass down an extra *errstr for
their respective callers.  That's an ABI breakage but I'd like to
think that nobody uses that out-of-core.  (I need to double-check this
part, as well).
- HMAC (hmac_openssl.c and hmac.c) could use the same infra, but I did
not see a use for that yet.  It is possible to compile HMACs with MD5s,
but we don't have any in-core callers, and failures are just part of
the SCRAM workflow with dedicated error messages.

I am still not sure about the FIPS part, as per the argument of
OpenSSL using something different in 3.0.0, and Fedora that patches 
upstream in its own way, but this could be extended in
cryptohash_openssl.c to provide even more context.  For now, this
allows reports about OpenSSL internal failures, taking care of the
disturbance.

Thoughts?
--
Michael
From 064ef97fd2a171485ff408550380515493ce0072 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@xxxxxxxxxxx>
Date: Thu, 6 Jan 2022 17:13:55 +0900
Subject: [PATCH] Improve error reporting for cryptohashes

---
 src/include/common/cryptohash.h           |  1 +
 src/include/common/md5.h                  |  8 ++-
 src/backend/libpq/auth.c                  | 14 +++--
 src/backend/libpq/crypt.c                 | 20 ++++---
 src/backend/replication/backup_manifest.c |  9 ++-
 src/backend/utils/adt/cryptohashfuncs.c   | 25 ++++++---
 src/common/cryptohash.c                   | 57 ++++++++++++++++++-
 src/common/cryptohash_openssl.c           | 68 +++++++++++++++++++++++
 src/common/md5_common.c                   | 20 +++++--
 src/interfaces/libpq/fe-auth.c            | 22 ++++++--
 contrib/uuid-ossp/uuid-ossp.c             | 18 ++++--
 11 files changed, 219 insertions(+), 43 deletions(-)

diff --git a/src/include/common/cryptohash.h b/src/include/common/cryptohash.h
index 541dc844c8..61d74e1195 100644
--- a/src/include/common/cryptohash.h
+++ b/src/include/common/cryptohash.h
@@ -34,5 +34,6 @@ extern int	pg_cryptohash_init(pg_cryptohash_ctx *ctx);
 extern int	pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len);
 extern int	pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len);
 extern void pg_cryptohash_free(pg_cryptohash_ctx *ctx);
+extern char *pg_cryptohash_error(pg_cryptohash_ctx *ctx);
 
 #endif							/* PG_CRYPTOHASH_H */
diff --git a/src/include/common/md5.h b/src/include/common/md5.h
index 62a31e6ed4..1db2855d95 100644
--- a/src/include/common/md5.h
+++ b/src/include/common/md5.h
@@ -26,9 +26,11 @@
 #define MD5_PASSWD_LEN	35
 
 /* Utilities common to all the MD5 implementations, as of md5_common.c */
-extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum);
-extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf);
+extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum,
+						char **errstr);
+extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf,
+						  char **errstr);
 extern bool pg_md5_encrypt(const char *passwd, const char *salt,
-						   size_t salt_len, char *buf);
+						   size_t salt_len, char *buf, char **errstr);
 
 #endif							/* PG_MD5_H */
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 7bcf52523b..9f118ec546 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -3085,6 +3085,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 	md5trailer = packet->vector;
 	for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
 	{
+		char	   *errstr = NULL;
+
 		memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
 
 		/*
@@ -3093,10 +3095,12 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 		 */
 		md5trailer = encryptedpassword + i;
 
-		if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
+		if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH,
+						   encryptedpassword + i, &errstr))
 		{
 			ereport(LOG,
-					(errmsg("could not perform MD5 encryption of password")));
+					(errmsg("could not perform MD5 encryption of password: %s",
+							errstr)));
 			pfree(cryptvector);
 			pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
 			return STATUS_ERROR;
@@ -3181,6 +3185,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 		struct timeval timeout;
 		struct timeval now;
 		int64		timeoutval;
+		char	   *errstr;
 
 		gettimeofday(&now, NULL);
 		timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec);
@@ -3299,10 +3304,11 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 
 		if (!pg_md5_binary(cryptvector,
 						   packetlength + strlen(secret),
-						   encryptedpassword))
+						   encryptedpassword, &errstr))
 		{
 			ereport(LOG,
-					(errmsg("could not perform MD5 encryption of received packet")));
+					(errmsg("could not perform MD5 encryption of received packet: %s",
+							errstr)));
 			pfree(cryptvector);
 			continue;
 		}
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index 3fcad991a7..396510e7a5 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -116,6 +116,7 @@ encrypt_password(PasswordType target_type, const char *role,
 {
 	PasswordType guessed_type = get_password_type(password);
 	char	   *encrypted_password;
+	char	   *errstr = NULL;
 
 	if (guessed_type != PASSWORD_TYPE_PLAINTEXT)
 	{
@@ -132,8 +133,8 @@ encrypt_password(PasswordType target_type, const char *role,
 			encrypted_password = palloc(MD5_PASSWD_LEN + 1);
 
 			if (!pg_md5_encrypt(password, role, strlen(role),
-								encrypted_password))
-				elog(ERROR, "password encryption failed");
+								encrypted_password, &errstr))
+				elog(ERROR, "password encryption failed: %s", errstr);
 			return encrypted_password;
 
 		case PASSWORD_TYPE_SCRAM_SHA_256:
@@ -170,6 +171,7 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
 {
 	int			retval;
 	char		crypt_pwd[MD5_PASSWD_LEN + 1];
+	char	   *errstr = NULL;
 
 	Assert(md5_salt_len > 0);
 
@@ -185,13 +187,13 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
 	 * Compute the correct answer for the MD5 challenge.
 	 *
 	 * We do not bother setting logdetail for any pg_md5_encrypt failure
-	 * below: the only possible error is out-of-memory, which is unlikely, and
-	 * if it did happen adding a psprintf call would only make things worse.
+	 * below: an out-of-memory, which is unlikely, is possible and if it
+	 * did happen adding a psprintf call would only make things worse.
 	 */
 	/* stored password already encrypted, only do salt */
 	if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
 						md5_salt, md5_salt_len,
-						crypt_pwd))
+						crypt_pwd, &errstr))
 	{
 		return STATUS_ERROR;
 	}
@@ -224,6 +226,7 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
 				   char **logdetail)
 {
 	char		crypt_client_pass[MD5_PASSWD_LEN + 1];
+	char	   *errstr = NULL;
 
 	/*
 	 * Client sent password in plaintext.  If we have an MD5 hash stored, hash
@@ -251,12 +254,13 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
 			if (!pg_md5_encrypt(client_pass,
 								role,
 								strlen(role),
-								crypt_client_pass))
+								crypt_client_pass,
+								&errstr))
 			{
 				/*
 				 * We do not bother setting logdetail for pg_md5_encrypt
-				 * failure: the only possible error is out-of-memory, which is
-				 * unlikely, and if it did happen adding a psprintf call would
+				 * failure: out-of-memory, which is unlikely, is a possible
+				 * outcome and if it did happen adding a psprintf call would
 				 * only make things worse.
 				 */
 				return STATUS_ERROR;
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index 4fe11a3b5c..f29d66b719 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -68,7 +68,8 @@ InitializeBackupManifest(backup_manifest_info *manifest,
 		manifest->buffile = BufFileCreateTemp(false);
 		manifest->manifest_ctx = pg_cryptohash_create(PG_SHA256);
 		if (pg_cryptohash_init(manifest->manifest_ctx) < 0)
-			elog(ERROR, "failed to initialize checksum of backup manifest");
+			elog(ERROR, "failed to initialize checksum of backup manifest: %s",
+				 pg_cryptohash_error(manifest->manifest_ctx));
 	}
 
 	manifest->manifest_size = UINT64CONST(0);
@@ -334,7 +335,8 @@ SendBackupManifest(backup_manifest_info *manifest, bbsink *sink)
 	manifest->still_checksumming = false;
 	if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf,
 							sizeof(checksumbuf)) < 0)
-		elog(ERROR, "failed to finalize checksum of backup manifest");
+		elog(ERROR, "failed to finalize checksum of backup manifest: %s",
+			 pg_cryptohash_error(manifest->manifest_ctx));
 	AppendStringToManifest(manifest, "\"Manifest-Checksum\": \"");
 
 	hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf);
@@ -391,7 +393,8 @@ AppendStringToManifest(backup_manifest_info *manifest, char *s)
 	if (manifest->still_checksumming)
 	{
 		if (pg_cryptohash_update(manifest->manifest_ctx, (uint8 *) s, len) < 0)
-			elog(ERROR, "failed to update checksum of backup manifest");
+			elog(ERROR, "failed to update checksum of backup manifest: %s",
+				 pg_cryptohash_error(manifest->manifest_ctx));
 	}
 	BufFileWrite(manifest->buffile, s, len);
 	manifest->manifest_size += len;
diff --git a/src/backend/utils/adt/cryptohashfuncs.c b/src/backend/utils/adt/cryptohashfuncs.c
index 6a0f0258e6..a972a05fcf 100644
--- a/src/backend/utils/adt/cryptohashfuncs.c
+++ b/src/backend/utils/adt/cryptohashfuncs.c
@@ -35,15 +35,17 @@ md5_text(PG_FUNCTION_ARGS)
 	text	   *in_text = PG_GETARG_TEXT_PP(0);
 	size_t		len;
 	char		hexsum[MD5_HASH_LEN + 1];
+	char	   *errstr = NULL;
 
 	/* Calculate the length of the buffer using varlena metadata */
 	len = VARSIZE_ANY_EXHDR(in_text);
 
 	/* get the hash result */
-	if (pg_md5_hash(VARDATA_ANY(in_text), len, hexsum) == false)
+	if (pg_md5_hash(VARDATA_ANY(in_text), len, hexsum, &errstr) == false)
 		ereport(ERROR,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not compute %s hash: %s", "MD5",
+						errstr)));
 
 	/* convert to text and return it */
 	PG_RETURN_TEXT_P(cstring_to_text(hexsum));
@@ -58,12 +60,14 @@ md5_bytea(PG_FUNCTION_ARGS)
 	bytea	   *in = PG_GETARG_BYTEA_PP(0);
 	size_t		len;
 	char		hexsum[MD5_HASH_LEN + 1];
+	char	   *errstr = NULL;
 
 	len = VARSIZE_ANY_EXHDR(in);
-	if (pg_md5_hash(VARDATA_ANY(in), len, hexsum) == false)
+	if (pg_md5_hash(VARDATA_ANY(in), len, hexsum, &errstr) == false)
 		ereport(ERROR,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not compute %s hash: %s", "MD5",
+						errstr)));
 
 	PG_RETURN_TEXT_P(cstring_to_text(hexsum));
 }
@@ -111,12 +115,15 @@ cryptohash_internal(pg_cryptohash_type type, bytea *input)
 
 	ctx = pg_cryptohash_create(type);
 	if (pg_cryptohash_init(ctx) < 0)
-		elog(ERROR, "could not initialize %s context", typestr);
+		elog(ERROR, "could not initialize %s context: %s", typestr,
+			 pg_cryptohash_error(ctx));
 	if (pg_cryptohash_update(ctx, data, len) < 0)
-		elog(ERROR, "could not update %s context", typestr);
+		elog(ERROR, "could not update %s context: %s", typestr,
+			 pg_cryptohash_error(ctx));
 	if (pg_cryptohash_final(ctx, (unsigned char *) VARDATA(result),
 							digest_len) < 0)
-		elog(ERROR, "could not finalize %s context", typestr);
+		elog(ERROR, "could not finalize %s context: %s", typestr,
+			 pg_cryptohash_error(ctx));
 	pg_cryptohash_free(ctx);
 
 	SET_VARSIZE(result, digest_len + VARHDRSZ);
diff --git a/src/common/cryptohash.c b/src/common/cryptohash.c
index 0dab74a094..1115365c85 100644
--- a/src/common/cryptohash.c
+++ b/src/common/cryptohash.c
@@ -40,10 +40,18 @@
 #define FREE(ptr) free(ptr)
 #endif
 
+/* Set of error states */
+typedef enum pg_cryptohash_errno
+{
+	PG_CRYPTOHASH_ERROR_NONE = 0,
+	PG_CRYPTOHASH_ERROR_DEST_LEN
+} pg_cryptohash_errno;
+
 /* Internal pg_cryptohash_ctx structure */
 struct pg_cryptohash_ctx
 {
 	pg_cryptohash_type type;
+	pg_cryptohash_errno	error;
 
 	union
 	{
@@ -76,9 +84,10 @@ pg_cryptohash_create(pg_cryptohash_type type)
 	ctx = ALLOC(sizeof(pg_cryptohash_ctx));
 	if (ctx == NULL)
 		return NULL;
+
 	memset(ctx, 0, sizeof(pg_cryptohash_ctx));
 	ctx->type = type;
-
+	ctx->error = PG_CRYPTOHASH_ERROR_NONE;
 	return ctx;
 }
 
@@ -174,32 +183,50 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
 	{
 		case PG_MD5:
 			if (len < MD5_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			pg_md5_final(&ctx->data.md5, dest);
 			break;
 		case PG_SHA1:
 			if (len < SHA1_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			pg_sha1_final(&ctx->data.sha1, dest);
 			break;
 		case PG_SHA224:
 			if (len < PG_SHA224_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			pg_sha224_final(&ctx->data.sha224, dest);
 			break;
 		case PG_SHA256:
 			if (len < PG_SHA256_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			pg_sha256_final(&ctx->data.sha256, dest);
 			break;
 		case PG_SHA384:
 			if (len < PG_SHA384_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			pg_sha384_final(&ctx->data.sha384, dest);
 			break;
 		case PG_SHA512:
 			if (len < PG_SHA512_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			pg_sha512_final(&ctx->data.sha512, dest);
 			break;
 	}
@@ -221,3 +248,31 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx)
 	explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
 	FREE(ctx);
 }
+
+/*
+ * pg_cryptohash_error
+ *
+ * Returns a static string providing errors about an error that
+ * happened during a computation.
+ */
+char *
+pg_cryptohash_error(pg_cryptohash_ctx *ctx)
+{
+	/*
+	 * This implementation would never fail because of an out-of-memory
+	 * error, except when creating the context.
+	 */
+	if (ctx == NULL)
+		return _("out of memory");
+
+	switch (ctx->error)
+	{
+		case PG_CRYPTOHASH_ERROR_NONE:
+			return _("success");
+		case PG_CRYPTOHASH_ERROR_DEST_LEN:
+			return _("destination buffer too small");
+	}
+
+	Assert(false);
+	return NULL;
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 643cc7aea2..12843809ca 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -46,6 +46,14 @@
 #define FREE(ptr) free(ptr)
 #endif
 
+/* Set of error states */
+typedef enum pg_cryptohash_errno
+{
+	PG_CRYPTOHASH_ERROR_NONE = 0,
+	PG_CRYPTOHASH_ERROR_DEST_LEN,
+	PG_CRYPTOHASH_ERROR_OPENSSL
+} pg_cryptohash_errno;
+
 /*
  * Internal pg_cryptohash_ctx structure.
  *
@@ -55,6 +63,7 @@
 struct pg_cryptohash_ctx
 {
 	pg_cryptohash_type type;
+	pg_cryptohash_errno error;
 
 	EVP_MD_CTX *evpctx;
 
@@ -88,6 +97,7 @@ pg_cryptohash_create(pg_cryptohash_type type)
 		return NULL;
 	memset(ctx, 0, sizeof(pg_cryptohash_ctx));
 	ctx->type = type;
+	ctx->error = PG_CRYPTOHASH_ERROR_NONE;
 
 	/*
 	 * Initialization takes care of assigning the correct type for OpenSSL.
@@ -153,7 +163,10 @@ pg_cryptohash_init(pg_cryptohash_ctx *ctx)
 
 	/* OpenSSL internals return 1 on success, 0 on failure */
 	if (status <= 0)
+	{
+		ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
 		return -1;
+	}
 	return 0;
 }
 
@@ -174,7 +187,10 @@ pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
 
 	/* OpenSSL internals return 1 on success, 0 on failure */
 	if (status <= 0)
+	{
+		ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
 		return -1;
+	}
 	return 0;
 }
 
@@ -195,27 +211,45 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
 	{
 		case PG_MD5:
 			if (len < MD5_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			break;
 		case PG_SHA1:
 			if (len < SHA1_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			break;
 		case PG_SHA224:
 			if (len < PG_SHA224_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			break;
 		case PG_SHA256:
 			if (len < PG_SHA256_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			break;
 		case PG_SHA384:
 			if (len < PG_SHA384_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			break;
 		case PG_SHA512:
 			if (len < PG_SHA512_DIGEST_LENGTH)
+			{
+				ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
 				return -1;
+			}
 			break;
 	}
 
@@ -223,7 +257,10 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
 
 	/* OpenSSL internals return 1 on success, 0 on failure */
 	if (status <= 0)
+	{
+		ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL;
 		return -1;
+	}
 	return 0;
 }
 
@@ -248,3 +285,34 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx)
 	explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
 	FREE(ctx);
 }
+
+/*
+ * pg_cryptohash_error
+ *
+ * Returns a static string providing errors about an error that
+ * happened during a computation.
+ */
+char *
+pg_cryptohash_error(pg_cryptohash_ctx *ctx)
+{
+	/*
+	 * This implementation would never fail because of an out-of-memory
+	 * error, except when creating the context.
+	 */
+	if (ctx == NULL)
+		return _("out of memory");
+
+	switch (ctx->error)
+	{
+		case PG_CRYPTOHASH_ERROR_NONE:
+			return _("success");
+		case PG_CRYPTOHASH_ERROR_DEST_LEN:
+			return _("destination buffer too small");
+		case PG_CRYPTOHASH_ERROR_OPENSSL:
+			return _("OpenSSL failure");
+	}
+
+	/* assume that the default is out-of-memory, anyway */
+	Assert(false);
+	return NULL;
+}
diff --git a/src/common/md5_common.c b/src/common/md5_common.c
index 2114890eff..18041a5552 100644
--- a/src/common/md5_common.c
+++ b/src/common/md5_common.c
@@ -67,7 +67,7 @@ bytesToHex(uint8 b[16], char *s)
  */
 
 bool
-pg_md5_hash(const void *buff, size_t len, char *hexsum)
+pg_md5_hash(const void *buff, size_t len, char *hexsum, char **errstr)
 {
 	uint8		sum[MD5_DIGEST_LENGTH];
 	pg_cryptohash_ctx *ctx;
@@ -80,6 +80,7 @@ pg_md5_hash(const void *buff, size_t len, char *hexsum)
 		pg_cryptohash_update(ctx, buff, len) < 0 ||
 		pg_cryptohash_final(ctx, sum, sizeof(sum)) < 0)
 	{
+		*errstr = pg_cryptohash_error(ctx);
 		pg_cryptohash_free(ctx);
 		return false;
 	}
@@ -90,18 +91,23 @@ pg_md5_hash(const void *buff, size_t len, char *hexsum)
 }
 
 bool
-pg_md5_binary(const void *buff, size_t len, void *outbuf)
+pg_md5_binary(const void *buff, size_t len, void *outbuf, char **errstr)
 {
 	pg_cryptohash_ctx *ctx;
 
+	*errstr = NULL;
 	ctx = pg_cryptohash_create(PG_MD5);
 	if (ctx == NULL)
+	{
+		*errstr = pg_cryptohash_error(NULL);	/* returns OOM */
 		return false;
+	}
 
 	if (pg_cryptohash_init(ctx) < 0 ||
 		pg_cryptohash_update(ctx, buff, len) < 0 ||
 		pg_cryptohash_final(ctx, outbuf, MD5_DIGEST_LENGTH) < 0)
 	{
+		*errstr = pg_cryptohash_error(ctx);
 		pg_cryptohash_free(ctx);
 		return false;
 	}
@@ -118,11 +124,12 @@ pg_md5_binary(const void *buff, size_t len, void *outbuf)
  * Output format is "md5" followed by a 32-hex-digit MD5 checksum.
  * Hence, the output buffer "buf" must be at least 36 bytes long.
  *
- * Returns true if okay, false on error (out of memory).
+ * Returns true if okay, false on error with *errstr providing some
+ * error context.
  */
 bool
 pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len,
-			   char *buf)
+			   char *buf, char **errstr)
 {
 	size_t		passwd_len = strlen(passwd);
 
@@ -131,7 +138,10 @@ pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len,
 	bool		ret;
 
 	if (!crypt_buf)
+	{
+		*errstr = _("out of memory");
 		return false;
+	}
 
 	/*
 	 * Place salt at the end because it may be known by users trying to crack
@@ -141,7 +151,7 @@ pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len,
 	memcpy(crypt_buf + passwd_len, salt, salt_len);
 
 	strcpy(buf, "md5");
-	ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3);
+	ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3, errstr);
 
 	free(crypt_buf);
 
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 3421ed4685..45c28bfb17 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -790,6 +790,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 		case AUTH_REQ_MD5:
 			{
 				char	   *crypt_pwd2;
+				char	   *errstr;
 
 				/* Allocate enough space for two MD5 hashes */
 				crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
@@ -802,14 +803,21 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 
 				crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
 				if (!pg_md5_encrypt(password, conn->pguser,
-									strlen(conn->pguser), crypt_pwd2))
+									strlen(conn->pguser), crypt_pwd2,
+									&errstr))
 				{
+					appendPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("could not encrypt password: %s\n"),
+									  errstr);
 					free(crypt_pwd);
 					return STATUS_ERROR;
 				}
 				if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), md5Salt,
-									4, crypt_pwd))
+									4, crypt_pwd, &errstr))
 				{
+					appendPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("could not encrypt password: %s\n"),
+									  errstr);
 					free(crypt_pwd);
 					return STATUS_ERROR;
 				}
@@ -1175,12 +1183,13 @@ char *
 PQencryptPassword(const char *passwd, const char *user)
 {
 	char	   *crypt_pwd;
+	char	   *errstr = NULL;
 
 	crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
 	if (!crypt_pwd)
 		return NULL;
 
-	if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
+	if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr))
 	{
 		free(crypt_pwd);
 		return NULL;
@@ -1287,8 +1296,13 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
 		crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
 		if (crypt_pwd)
 		{
-			if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
+			char	   *errstr = NULL;
+
+			if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr))
 			{
+				appendPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not encrypt password: %s\n"),
+								  errstr);
 				free(crypt_pwd);
 				crypt_pwd = NULL;
 			}
diff --git a/contrib/uuid-ossp/uuid-ossp.c b/contrib/uuid-ossp/uuid-ossp.c
index f9335f2863..f52f007a08 100644
--- a/contrib/uuid-ossp/uuid-ossp.c
+++ b/contrib/uuid-ossp/uuid-ossp.c
@@ -319,14 +319,17 @@ uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
 					pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_MD5);
 
 					if (pg_cryptohash_init(ctx) < 0)
-						elog(ERROR, "could not initialize %s context", "MD5");
+						elog(ERROR, "could not initialize %s context: %s", "MD5",
+							 pg_cryptohash_error(ctx));
 					if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
 						pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
-						elog(ERROR, "could not update %s context", "MD5");
+						elog(ERROR, "could not update %s context: %s", "MD5",
+							 pg_cryptohash_error(ctx));
 					/* we assume sizeof MD5 result is 16, same as UUID size */
 					if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
 											sizeof(uu)) < 0)
-						elog(ERROR, "could not finalize %s context", "MD5");
+						elog(ERROR, "could not finalize %s context: %s", "MD5",
+							 pg_cryptohash_error(ctx));
 					pg_cryptohash_free(ctx);
 				}
 				else
@@ -335,12 +338,15 @@ uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
 					unsigned char sha1result[SHA1_DIGEST_LENGTH];
 
 					if (pg_cryptohash_init(ctx) < 0)
-						elog(ERROR, "could not initialize %s context", "SHA1");
+						elog(ERROR, "could not initialize %s context: %s", "SHA1",
+							 pg_cryptohash_error(ctx));
 					if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
 						pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
-						elog(ERROR, "could not update %s context", "SHA1");
+						elog(ERROR, "could not update %s context: %s", "SHA1",
+							 pg_cryptohash_error(ctx));
 					if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
-						elog(ERROR, "could not finalize %s context", "SHA1");
+						elog(ERROR, "could not finalize %s context: %s", "SHA1",
+							 pg_cryptohash_error(ctx));
 					pg_cryptohash_free(ctx);
 
 					memcpy(&uu, sha1result, sizeof(uu));
-- 
2.34.1

Attachment: signature.asc
Description: PGP signature


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Postgresql Jobs]     [Postgresql Admin]     [Postgresql Performance]     [Linux Clusters]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Databases]     [Postgresql & PHP]     [Yosemite]

  Powered by Linux