#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,