From c55b72d51db8c6ec91b99bab8b4f6bacdaeefbab Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Mon, 25 Jul 2016 14:40:15 +0900
Subject: [PATCH 5/8] Create generic routine to fetch password and valid until
 values for a role

This is used now for the MD5-encrypted case and the plain text, and this
is going to be used as well for SCRAM-SHA256. That's as well useful for
any new password-based protocols.
---
 src/backend/libpq/crypt.c | 59 +++++++++++++++++++++++++++++++++++------------
 src/include/libpq/crypt.h |  2 ++
 2 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index d84a180..1c41c57 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -1,8 +1,8 @@
 /*-------------------------------------------------------------------------
  *
  * crypt.c
- *	  Look into the password file and check the encrypted password with
- *	  the one passed in from the frontend.
+ *	  Set of routines to look into the password file and check the
+ *	  encrypted password with the one passed in from the frontend.
  *
  * Original coding by Todd A. Brandys
  *
@@ -30,23 +30,25 @@
 
 
 /*
- * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
- * In the error case, optionally store a palloc'd string at *logdetail
- * that will be sent to the postmaster log (but not the client).
+ * Fetch information of a given role necessary to check password data,
+ * and return STATUS_OK or STATUS_ERROR. In the case of an error,
+ * optionally store a palloc'd string at *logdetail that will be sent
+ * to the postmaster log (but not the client).
  */
 int
-md5_crypt_verify(const Port *port, const char *role, char *client_pass,
+get_role_details(const char *role,
+				 char **password,
+				 TimestampTz *vuntil,
+				 bool *vuntil_null,
 				 char **logdetail)
 {
-	int			retval = STATUS_ERROR;
-	char	   *shadow_pass,
-			   *crypt_pwd;
-	TimestampTz vuntil = 0;
-	char	   *crypt_client_pass = client_pass;
 	HeapTuple	roleTup;
 	Datum		datum;
 	bool		isnull;
 
+	*vuntil = 0;
+	*vuntil_null = true;
+
 	/* Get role info from pg_authid */
 	roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
 	if (!HeapTupleIsValid(roleTup))
@@ -65,22 +67,49 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
 							  role);
 		return STATUS_ERROR;	/* user has no password */
 	}
-	shadow_pass = TextDatumGetCString(datum);
+	*password = TextDatumGetCString(datum);
 
 	datum = SysCacheGetAttr(AUTHNAME, roleTup,
 							Anum_pg_authid_rolvaliduntil, &isnull);
 	if (!isnull)
-		vuntil = DatumGetTimestampTz(datum);
+	{
+		*vuntil = DatumGetTimestampTz(datum);
+		*vuntil_null = false;
+	}
 
 	ReleaseSysCache(roleTup);
 
-	if (*shadow_pass == '\0')
+	if (**password == '\0')
 	{
 		*logdetail = psprintf(_("User \"%s\" has an empty password."),
 							  role);
 		return STATUS_ERROR;	/* empty password */
 	}
 
+	return STATUS_OK;
+}
+
+/*
+ * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
+ * In the error case, optionally store a palloc'd string at *logdetail
+ * that will be sent to the postmaster log (but not the client).
+ */
+int
+md5_crypt_verify(const Port *port, const char *role, char *client_pass,
+				 char **logdetail)
+{
+	int			retval = STATUS_ERROR;
+	char	   *shadow_pass,
+			   *crypt_pwd;
+	TimestampTz vuntil;
+	char	   *crypt_client_pass = client_pass;
+	bool		vuntil_null;
+
+	/* fetch details about role needed for password checks */
+	if (get_role_details(role, &shadow_pass, &vuntil, &vuntil_null,
+							  logdetail) != STATUS_OK)
+		return STATUS_ERROR;
+
 	/*
 	 * Compare with the encrypted or plain password depending on the
 	 * authentication method being used for this connection.  (We do not
@@ -152,7 +181,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
 		/*
 		 * Password OK, now check to be sure we are not past rolvaliduntil
 		 */
-		if (isnull)
+		if (vuntil_null)
 			retval = STATUS_OK;
 		else if (vuntil < GetCurrentTimestamp())
 		{
diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h
index 5725bb4..856c451 100644
--- a/src/include/libpq/crypt.h
+++ b/src/include/libpq/crypt.h
@@ -15,6 +15,8 @@
 
 #include "libpq/libpq-be.h"
 
+extern int get_role_details(const char *role, char **password,
+				 TimestampTz *vuntil, bool *vuntil_null, char **logdetail);
 extern int md5_crypt_verify(const Port *port, const char *role,
 				 char *client_pass, char **logdetail);
 
-- 
2.9.3

