From 7ef5de2d91741b0f2a416af1509a5c0027a3e7ed Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 14 Nov 2016 18:08:40 +0900
Subject: [PATCH 5/9] Refactor decision-making of password encryption into a
 single routine

This routine was duplicated for CREATE ROLE and ALTER ROLE, and while
there is little gain by doing it now if there is only plain password
and md5-encryption support, this eases the decision-making regarding
if and how a password needs to be encrypted if there are more protocols
supported, like SCRAM.
---
 src/backend/commands/user.c | 83 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 59 insertions(+), 24 deletions(-)

diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index adc6b99..def8ea1 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -55,6 +55,8 @@ static void AddRoleMems(const char *rolename, Oid roleid,
 static void DelRoleMems(const char *rolename, Oid roleid,
 			List *memberSpecs, List *memberIds,
 			bool admin_opt);
+static char *encrypt_password(char *passwd, char *rolname,
+			int passwd_type);
 
 
 /* Check if current user has createrole privileges */
@@ -64,6 +66,49 @@ have_createrole_privilege(void)
 	return has_createrole_privilege(GetUserId());
 }
 
+/*
+ * Encrypt a password if necessary for insertion in pg_authid.
+ *
+ * If a password is found as already MD5-encrypted, no error is raised
+ * to ease the dump and reload of such data. Returns a palloc'ed string
+ * holding the encrypted password if any transformation on the input
+ * string has been done.
+ */
+static char *
+encrypt_password(char *password, char *rolname, int passwd_type)
+{
+	char *res;
+
+	Assert(password != NULL);
+
+	/*
+	 * If a password is already identified as MD5-encrypted, it is used
+	 * as such.  If the password given is not encrypted, adapt it depending
+	 * on the type wanted by the caller of this routine.
+	 */
+	if (isMD5(password))
+		res = password;
+	else
+	{
+		switch (passwd_type)
+		{
+			case PASSWORD_TYPE_PLAINTEXT:
+				res = password;
+				break;
+			case PASSWORD_TYPE_MD5:
+				res = (char *) palloc(MD5_PASSWD_LEN + 1);
+				if (!pg_md5_encrypt(password, rolname,
+									strlen(rolname),
+									res))
+					elog(ERROR, "password encryption failed");
+				break;
+			default:
+				elog(ERROR, "incorrect password type");
+		}
+	}
+
+	return res;
+}
 
 /*
  * CREATE ROLE
@@ -81,7 +126,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	ListCell   *option;
 	char	   *password = NULL;	/* user password */
 	int			password_type = Password_encryption;
-	char		encrypted_password[MD5_PASSWD_LEN + 1];
+	char	   *encrypted_passwd;
 	bool		issuper = false;	/* Make the user a superuser? */
 	bool		inherit = true; /* Auto inherit privileges? */
 	bool		createrole = false;		/* Can this user create roles? */
@@ -393,17 +438,12 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 
 	if (password)
 	{
-		if (password_type == PASSWORD_TYPE_PLAINTEXT || isMD5(password))
-			new_record[Anum_pg_authid_rolpassword - 1] =
-				CStringGetTextDatum(password);
-		else
-		{
-			if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
-								encrypted_password))
-				elog(ERROR, "password encryption failed");
-			new_record[Anum_pg_authid_rolpassword - 1] =
-				CStringGetTextDatum(encrypted_password);
-		}
+		encrypted_passwd = encrypt_password(password,
+											stmt->role,
+											password_type);
+
+		new_record[Anum_pg_authid_rolpassword - 1] =
+			CStringGetTextDatum(encrypted_passwd);
 	}
 	else
 		new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
@@ -506,7 +546,7 @@ AlterRole(AlterRoleStmt *stmt)
 	char	   *rolename = NULL;
 	char	   *password = NULL;	/* user password */
 	int			password_type = Password_encryption;
-	char		encrypted_password[MD5_PASSWD_LEN + 1];
+	char	   *encrypted_passwd;
 	int			issuper = -1;	/* Make the user a superuser? */
 	int			inherit = -1;	/* Auto inherit privileges? */
 	int			createrole = -1;	/* Can this user create roles? */
@@ -804,17 +844,12 @@ AlterRole(AlterRoleStmt *stmt)
 	/* password */
 	if (password)
 	{
-		if (password_type == PASSWORD_TYPE_PLAINTEXT || isMD5(password))
-			new_record[Anum_pg_authid_rolpassword - 1] =
-				CStringGetTextDatum(password);
-		else
-		{
-			if (!pg_md5_encrypt(password, rolename, strlen(rolename),
-								encrypted_password))
-				elog(ERROR, "password encryption failed");
-			new_record[Anum_pg_authid_rolpassword - 1] =
-				CStringGetTextDatum(encrypted_password);
-		}
+		encrypted_passwd = encrypt_password(password,
+											rolename,
+											password_type);
+
+		new_record[Anum_pg_authid_rolpassword - 1] =
+			CStringGetTextDatum(encrypted_passwd);
 		new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
 	}
 
-- 
2.10.2

