From 12667b9848de6905478dae84581e4691ade5fe32 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Sat, 25 Mar 2017 14:07:16 +0900
Subject: [PATCH 4/4] Extend psql's \password and createuser to handle SCRAM
 verifier creation

Depending on the version of PostgreSQL those utilities are connected to,
generate MD5 verifiers when connecting to a server older than 10, and
MD5 otherwise.
---
 doc/src/sgml/ref/createuser.sgml |  6 ++++--
 doc/src/sgml/ref/psql-ref.sgml   |  4 +++-
 src/bin/psql/command.c           |  9 ++++++++-
 src/bin/scripts/createuser.c     | 16 +++++++++++++---
 4 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/ref/createuser.sgml b/doc/src/sgml/ref/createuser.sgml
index 4332008c68..4454591d79 100644
--- a/doc/src/sgml/ref/createuser.sgml
+++ b/doc/src/sgml/ref/createuser.sgml
@@ -125,7 +125,9 @@ PostgreSQL documentation
       <listitem>
        <para>
         Encrypts the user's password stored in the database. If not
-        specified, the default password behavior is used.
+        specified, the default password behavior is used. The password
+        is hashed using SCRAM-SHA-256 when connecting to a version of
+        <productname>PostgreSQL</> newer than 10, and MD5 otherwise.
        </para>
       </listitem>
      </varlistentry>
@@ -477,7 +479,7 @@ PostgreSQL documentation
 <prompt>$ </prompt><userinput>createuser -P -s -e joe</userinput>
 <computeroutput>Enter password for new role: </computeroutput><userinput>xyzzy</userinput>
 <computeroutput>Enter it again: </computeroutput><userinput>xyzzy</userinput>
-<computeroutput>CREATE ROLE joe PASSWORD 'md5b5f5ba1a423792b526f799ae4eb3d59e' SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;</computeroutput>
+<computeroutput>CREATE ROLE joe PASSWORD 'scram-sha-256:aPheg4yCAFhStg==:4096:TOHLPF8w+NzqtcxD2oz9w5wPISutHLOXWMKoe4HCvuo=:lTG0OiB/ZH5/hsfUqnndRwfMziY5j5C6FS8IAIwL2nA=' SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;</computeroutput>
 </screen>
     In the above example, the new password isn't actually echoed when typed,
     but we show what was typed for clarity.  As you see, the password is
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 3b86612862..749221aa76 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -2380,7 +2380,9 @@ lo_import 152801
         user).  This command prompts for the new password, encrypts it, and
         sends it to the server as an <command>ALTER ROLE</> command.  This
         makes sure that the new password does not appear in cleartext in the
-        command history, the server log, or elsewhere.
+        command history, the server log, or elsewhere. The password is hashed
+        with SCRAM-SHA-256 when connecting to <productname>PostgreSQL</> 10
+        and newer versions, and with MD5 otherwise.
         </para>
         </listitem>
       </varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ccea7a5fd0..4d575657d4 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1878,7 +1878,14 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
 			else
 				user = PQuser(pset.db);
 
-			encrypted_password = PQencryptPassword(pw1, user, "md5");
+			/*
+			 * Hash password using SCRAM-SHA-256 when connecting to servers
+			 * newer than Postgres 10, and hash with MD5 otherwise.
+			 */
+			if (pset.sversion < 100000)
+				encrypted_password = PQencryptPassword(pw1, user, "md5");
+			else
+				encrypted_password = PQencryptPassword(pw1, user, "scram");
 
 			if (!encrypted_password)
 			{
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 5af263e34a..0107497e23 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -274,9 +274,19 @@ main(int argc, char *argv[])
 		{
 			char	   *encrypted_password;
 
-			encrypted_password = PQencryptPassword(newpassword,
-												   newuser,
-												   "md5");
+			/*
+			 * Hash password using SCRAM-SHA-256 when connecting to servers
+			 * newer than Postgres 10, and hash with MD5 otherwise.
+			 */
+			if (PQserverVersion(conn) < 100000)
+				encrypted_password = PQencryptPassword(newpassword,
+													   newuser,
+													   "md5");
+			else
+				encrypted_password = PQencryptPassword(newpassword,
+													   newuser,
+													   "scram");
+
 			if (!encrypted_password)
 			{
 				fprintf(stderr, _("Password encryption failed.\n"));
-- 
2.12.2

