#endif
 
 #include "commands/user.h"
-#include "common/md5.h"
+#include "libpq/crypt.h"
 #include "fmgr.h"
 
 PG_MODULE_MAGIC;
  */
 static void
 check_password(const char *username,
-              const char *password,
-              int password_type,
+              const char *shadow_pass,
+              PasswordType password_type,
               Datum validuntil_time,
               bool validuntil_null)
 {
-   int         namelen = strlen(username);
-   int         pwdlen = strlen(password);
-   char        encrypted[MD5_PASSWD_LEN + 1];
-   int         i;
-   bool        pwd_has_letter,
-               pwd_has_nonletter;
-
-   switch (password_type)
+   if (password_type != PASSWORD_TYPE_PLAINTEXT)
    {
-       case PASSWORD_TYPE_MD5:
-
-           /*
-            * Unfortunately we cannot perform exhaustive checks on encrypted
-            * passwords - we are restricted to guessing. (Alternatively, we
-            * could insist on the password being presented non-encrypted, but
-            * that has its own security disadvantages.)
-            *
-            * We only check for username = password.
-            */
-           if (!pg_md5_encrypt(username, username, namelen, encrypted))
-               elog(ERROR, "password encryption failed");
-           if (strcmp(password, encrypted) == 0)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("password must not contain user name")));
-           break;
-
-       case PASSWORD_TYPE_PLAINTEXT:
-
+       /*
+        * Unfortunately we cannot perform exhaustive checks on encrypted
+        * passwords - we are restricted to guessing. (Alternatively, we could
+        * insist on the password being presented non-encrypted, but that has
+        * its own security disadvantages.)
+        *
+        * We only check for username = password.
+        */
+       char       *logdetail;
+
+       if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                    errmsg("password must not contain user name")));
+   }
+   else
+   {
+       /*
+        * For unencrypted passwords we can perform better checks
+        */
+       const char *password = shadow_pass;
+       int         pwdlen = strlen(password);
+       int         i;
+       bool        pwd_has_letter,
+                   pwd_has_nonletter;
+
+       /* enforce minimum length */
+       if (pwdlen < MIN_PWD_LENGTH)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                    errmsg("password is too short")));
+
+       /* check if the password contains the username */
+       if (strstr(password, username))
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                    errmsg("password must not contain user name")));
+
+       /* check if the password contains both letters and non-letters */
+       pwd_has_letter = false;
+       pwd_has_nonletter = false;
+       for (i = 0; i < pwdlen; i++)
+       {
            /*
-            * For unencrypted passwords we can perform better checks
+            * isalpha() does not work for multibyte encodings but let's
+            * consider non-ASCII characters non-letters
             */
-
-           /* enforce minimum length */
-           if (pwdlen < MIN_PWD_LENGTH)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("password is too short")));
-
-           /* check if the password contains the username */
-           if (strstr(password, username))
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("password must not contain user name")));
-
-           /* check if the password contains both letters and non-letters */
-           pwd_has_letter = false;
-           pwd_has_nonletter = false;
-           for (i = 0; i < pwdlen; i++)
-           {
-               /*
-                * isalpha() does not work for multibyte encodings but let's
-                * consider non-ASCII characters non-letters
-                */
-               if (isalpha((unsigned char) password[i]))
-                   pwd_has_letter = true;
-               else
-                   pwd_has_nonletter = true;
-           }
-           if (!pwd_has_letter || !pwd_has_nonletter)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-               errmsg("password must contain both letters and nonletters")));
+           if (isalpha((unsigned char) password[i]))
+               pwd_has_letter = true;
+           else
+               pwd_has_nonletter = true;
+       }
+       if (!pwd_has_letter || !pwd_has_nonletter)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+              errmsg("password must contain both letters and nonletters")));
 
 #ifdef USE_CRACKLIB
-           /* call cracklib to check password */
-           if (FascistCheck(password, CRACKLIB_DICTPATH))
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("password is easily cracked")));
+       /* call cracklib to check password */
+       if (FascistCheck(password, CRACKLIB_DICTPATH))
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                    errmsg("password is easily cracked")));
 #endif
-           break;
-
-       default:
-           elog(ERROR, "unrecognized password type: %d", password_type);
-           break;
    }
 
    /* all checks passed, password is ok */
 
 #include "commands/dbcommands.h"
 #include "commands/seclabel.h"
 #include "commands/user.h"
-#include "common/md5.h"
+#include "libpq/crypt.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
 #include "utils/acl.h"
    ListCell   *option;
    char       *password = NULL;    /* user password */
    int         password_type = Password_encryption;
-   char        encrypted_password[MD5_PASSWD_LEN + 1];
    bool        issuper = false;    /* Make the user a superuser? */
    bool        inherit = true; /* Auto inherit privileges? */
    bool        createrole = false;     /* Can this user create roles? */
    if (check_password_hook && password)
        (*check_password_hook) (stmt->role,
                                password,
-              isMD5(password) ? PASSWORD_TYPE_MD5 : PASSWORD_TYPE_PLAINTEXT,
+                               get_password_type(password),
                                validUntil_datum,
                                validUntil_null);
 
 
    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);
-       }
+       /* Encrypt the password to the requested format. */
+       char       *shadow_pass;
+
+       shadow_pass = encrypt_password(password_type, stmt->role, password);
+       new_record[Anum_pg_authid_rolpassword - 1] =
+           CStringGetTextDatum(shadow_pass);
    }
    else
        new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
    char       *rolename = NULL;
    char       *password = NULL;    /* user password */
    int         password_type = Password_encryption;
-   char        encrypted_password[MD5_PASSWD_LEN + 1];
    int         issuper = -1;   /* Make the user a superuser? */
    int         inherit = -1;   /* Auto inherit privileges? */
    int         createrole = -1;    /* Can this user create roles? */
    if (check_password_hook && password)
        (*check_password_hook) (rolename,
                                password,
-              isMD5(password) ? PASSWORD_TYPE_MD5 : PASSWORD_TYPE_PLAINTEXT,
+                               get_password_type(password),
                                validUntil_datum,
                                validUntil_null);
 
    /* 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);
-       }
+       /* Encrypt the password to the requested format. */
+       char       *shadow_pass;
+
+       shadow_pass = encrypt_password(password_type, rolename, password);
+       new_record[Anum_pg_authid_rolpassword - 1] =
+           CStringGetTextDatum(shadow_pass);
        new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
    }
 
 
    datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
 
-   if (!isnull && isMD5(TextDatumGetCString(datum)))
+   if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
    {
        /* MD5 uses the username as salt, so just clear it on a rename */
        repl_repl[Anum_pg_authid_rolpassword - 1] = true;
 
 /*-------------------------------------------------------------------------
  *
  * crypt.c
- *   Look into the password file and check the encrypted password with
- *   the one passed in from the frontend.
- *
- * Original coding by Todd A. Brandys
+ *   Functions for dealing with encrypted passwords stored in
+ *   pg_authid.rolpassword.
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
    return retval;
 }
 
+/*
+ * What kind of a password verifier is 'shadow_pass'?
+ */
+PasswordType
+get_password_type(const char *shadow_pass)
+{
+   if (strncmp(shadow_pass, "md5", 3) == 0 && strlen(shadow_pass) == MD5_PASSWD_LEN)
+       return PASSWORD_TYPE_MD5;
+   return PASSWORD_TYPE_PLAINTEXT;
+}
+
+/*
+ * Given a user-supplied password, convert it into a verifier of
+ * 'target_type' kind.
+ *
+ * If the password looks like a valid MD5 hash, it is stored as it is.
+ * We cannot reverse the hash, so even if the caller requested a plaintext
+ * plaintext password, the MD5 hash is returned.
+ */
+char *
+encrypt_password(PasswordType target_type, const char *role,
+                const char *password)
+{
+   PasswordType guessed_type = get_password_type(password);
+   char       *encrypted_password;
+
+   switch (target_type)
+   {
+       case PASSWORD_TYPE_PLAINTEXT:
+
+           /*
+            * We cannot convert a hashed password back to plaintext, so just
+            * store the password as it was, whether it was hashed or not.
+            */
+           return pstrdup(password);
+
+       case PASSWORD_TYPE_MD5:
+           switch (guessed_type)
+           {
+               case PASSWORD_TYPE_PLAINTEXT:
+                   encrypted_password = palloc(MD5_PASSWD_LEN + 1);
+
+                   if (!pg_md5_encrypt(password, role, strlen(role),
+                                       encrypted_password))
+                       elog(ERROR, "password encryption failed");
+                   return encrypted_password;
+
+               case PASSWORD_TYPE_MD5:
+                   return pstrdup(password);
+           }
+   }
+
+   /*
+    * This shouldn't happen, because the above switch statements should
+    * handle every combination of source and target password types.
+    */
+   elog(ERROR, "cannot encrypt password to requested type");
+}
+
 /*
  * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
  *
     * 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.
     */
-   if (isMD5(shadow_pass))
-   {
-       /* stored password already encrypted, only do salt */
-       if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
-                           md5_salt, md5_salt_len,
-                           crypt_pwd))
-       {
-           return STATUS_ERROR;
-       }
-   }
-   else
+   switch (get_password_type(shadow_pass))
    {
-       /* stored password is plain, double-encrypt */
-       if (!pg_md5_encrypt(shadow_pass,
-                           role,
-                           strlen(role),
-                           crypt_pwd2))
-       {
-           return STATUS_ERROR;
-       }
-       if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
-                           md5_salt, md5_salt_len,
-                           crypt_pwd))
-       {
+       case PASSWORD_TYPE_MD5:
+           /* stored password already encrypted, only do salt */
+           if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
+                               md5_salt, md5_salt_len,
+                               crypt_pwd))
+           {
+               return STATUS_ERROR;
+           }
+           break;
+
+       case PASSWORD_TYPE_PLAINTEXT:
+           /* stored password is plain, double-encrypt */
+           if (!pg_md5_encrypt(shadow_pass,
+                               role,
+                               strlen(role),
+                               crypt_pwd2))
+           {
+               return STATUS_ERROR;
+           }
+           if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
+                               md5_salt, md5_salt_len,
+                               crypt_pwd))
+           {
+               return STATUS_ERROR;
+           }
+           break;
+
+       default:
+           /* unknown password hash format. */
+           *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
+                                 role);
            return STATUS_ERROR;
-       }
    }
 
    if (strcmp(client_pass, crypt_pwd) == 0)
     * the password the client sent, and compare the hashes.  Otherwise
     * compare the plaintext passwords directly.
     */
-   if (isMD5(shadow_pass))
+   switch (get_password_type(shadow_pass))
    {
-       if (!pg_md5_encrypt(client_pass,
-                           role,
-                           strlen(role),
-                           crypt_client_pass))
-       {
+       case PASSWORD_TYPE_MD5:
+           if (!pg_md5_encrypt(client_pass,
+                               role,
+                               strlen(role),
+                               crypt_client_pass))
+           {
+               /*
+                * 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
+                * only make things worse.
+                */
+               return STATUS_ERROR;
+           }
+           client_pass = crypt_client_pass;
+           break;
+       case PASSWORD_TYPE_PLAINTEXT:
+           break;
+
+       default:
+
            /*
-            * 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 only make
-            * things worse.
+            * This shouldn't happen. Plain "password" authentication should
+            * be possible with any kind of stored password hash.
             */
+           *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
+                                 role);
            return STATUS_ERROR;
-       }
-       client_pass = crypt_client_pass;
    }
 
    if (strcmp(client_pass, shadow_pass) == 0)
 
 #define USER_H
 
 #include "catalog/objectaddress.h"
+#include "libpq/crypt.h"
 #include "nodes/parsenodes.h"
 #include "parser/parse_node.h"
 
-
-/*
- * Types of password, for Password_encryption GUC and the password_type
- * argument of the check-password hook.
- */
-typedef enum PasswordType
-{
-   PASSWORD_TYPE_PLAINTEXT = 0,
-   PASSWORD_TYPE_MD5
-} PasswordType;
-
-extern int Password_encryption;    /* GUC */
+/* GUC. Is actually of type PasswordType. */
+extern int Password_encryption;
 
 /* Hook to check passwords in CreateRole() and AlterRole() */
-typedef void (*check_password_hook_type) (const char *username, const char *password, int password_type, Datum validuntil_time, bool validuntil_null);
+typedef void (*check_password_hook_type) (const char *username, const char *shadow_pass, PasswordType password_type, Datum validuntil_time, bool validuntil_null);
 
 extern PGDLLIMPORT check_password_hook_type check_password_hook;
 
 
 
 #define MD5_PASSWD_LEN 35
 
-#define isMD5(passwd)  (strncmp(passwd, "md5", 3) == 0 && \
-                        strlen(passwd) == MD5_PASSWD_LEN)
-
-
 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_encrypt(const char *passwd, const char *salt,
 
 
 #include "datatype/timestamp.h"
 
-extern int get_role_password(const char *role, char **shadow_pass, char **logdetail);
+/*
+ * Types of password hashes or verifiers that can be stored in
+ * pg_authid.rolpassword.
+ *
+ * This is also used for the password_encryption GUC.
+ */
+typedef enum PasswordType
+{
+   PASSWORD_TYPE_PLAINTEXT = 0,
+   PASSWORD_TYPE_MD5
+} PasswordType;
+
+extern PasswordType get_password_type(const char *shadow_pass);
+extern char *encrypt_password(PasswordType target_type, const char *role,
+                const char *password);
+
+extern int get_role_password(const char *role, char **shadow_pass,
+                 char **logdetail);
 
 extern int md5_crypt_verify(const char *role, const char *shadow_pass,
                 const char *client_pass, const char *md5_salt,