From 35e22d1dead598e63be2ff2e6444ff2e282ea872 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 26 Sep 2016 13:51:15 +0900
Subject: [PATCH 4/8] 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 | 84 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 60 insertions(+), 24 deletions(-)

diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index fa3e984..2e89f1f 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,48 @@ 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.
+ */
+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 = pstrdup(password);
+	else
+	{
+		switch (passwd_type)
+		{
+			case PASSWORD_TYPE_PLAINTEXT:
+				res = pstrdup(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:
+				Assert(0);	/* should not come here */
+		}
+	}
+
+	return res;
+}
 
 /*
  * CREATE ROLE
@@ -81,7 +125,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 +437,13 @@ 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);
+		pfree(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,18 +844,14 @@ 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;
+		pfree(encrypted_passwd);
 	}
 
 	/* unset password */
-- 
2.9.3

