aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/crypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/crypt.c')
-rw-r--r--src/backend/libpq/crypt.c159
1 files changed, 119 insertions, 40 deletions
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index e1c10137d9f..893ce6e967b 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -1,10 +1,8 @@
/*-------------------------------------------------------------------------
*
* 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
@@ -106,6 +104,65 @@ get_role_password(const char *role, char **shadow_pass, char **logdetail)
}
/*
+ * 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.
*
* 'shadow_pass' is the user's correct password or password hash, as stored
@@ -135,32 +192,40 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
* 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)
@@ -198,22 +263,36 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
* 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)