/*
- * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
+ * Fetch stored password for a user, for authentication.
*
- * 'client_pass' is the password response given by the remote user. If
- * 'md5_salt' is not NULL, it is a response to an MD5 authentication
- * challenge, with the given salt. Otherwise, it is a plaintext password.
+ * Returns STATUS_OK on success. On error, returns STATUS_ERROR, and stores
+ * a palloc'd string describing the reason, for the postmaster log, in
+ * *logdetail. The error reason should *not* be sent to the client, to avoid
+ * giving away user information!
*
- * In the error case, optionally store a palloc'd string at *logdetail
- * that will be sent to the postmaster log (but not the client).
+ * If the password is expired, it is still returned in *shadow_pass, but the
+ * return code is STATUS_ERROR. On other errors, *shadow_pass is set to
+ * NULL.
*/
int
-md5_crypt_verify(const char *role, char *client_pass,
- char *md5_salt, int md5_salt_len, char **logdetail)
+get_role_password(const char *role, char **shadow_pass, char **logdetail)
{
int retval = STATUS_ERROR;
- char *shadow_pass,
- *crypt_pwd;
TimestampTz vuntil = 0;
- char *crypt_client_pass = client_pass;
HeapTuple roleTup;
Datum datum;
bool isnull;
+ *shadow_pass = NULL;
+
/* Get role info from pg_authid */
roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
if (!HeapTupleIsValid(roleTup))
role);
return STATUS_ERROR; /* user has no password */
}
- shadow_pass = TextDatumGetCString(datum);
+ *shadow_pass = TextDatumGetCString(datum);
datum = SysCacheGetAttr(AUTHNAME, roleTup,
Anum_pg_authid_rolvaliduntil, &isnull);
ReleaseSysCache(roleTup);
- if (*shadow_pass == '\0')
+ if (**shadow_pass == '\0')
{
*logdetail = psprintf(_("User \"%s\" has an empty password."),
role);
+ pfree(*shadow_pass);
+ *shadow_pass = NULL;
return STATUS_ERROR; /* empty password */
}
/*
- * Compare with the encrypted or plain password depending on the
- * authentication method being used for this connection. (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.)
+ * Password OK, now check to be sure we are not past rolvaliduntil
*/
- if (md5_salt)
+ if (isnull)
+ retval = STATUS_OK;
+ else if (vuntil < GetCurrentTimestamp())
{
- /* MD5 authentication */
- Assert(md5_salt_len > 0);
- crypt_pwd = palloc(MD5_PASSWD_LEN + 1);
- 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))
- {
- pfree(crypt_pwd);
- return STATUS_ERROR;
- }
- }
- else
+ *logdetail = psprintf(_("User \"%s\" has an expired password."),
+ role);
+ retval = STATUS_ERROR;
+ }
+ else
+ retval = STATUS_OK;
+
+ return retval;
+}
+
+/*
+ * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
+ *
+ * 'shadow_pass' is the user's correct password or password hash, as stored
+ * in pg_authid.rolpassword.
+ * 'client_pass' is the response given by the remote user to the MD5 challenge.
+ * 'md5_salt' is the salt used in the MD5 authentication challenge.
+ *
+ * In the error case, optionally store a palloc'd string at *logdetail
+ * that will be sent to the postmaster log (but not the client).
+ */
+int
+md5_crypt_verify(const char *role, const char *shadow_pass,
+ const char *client_pass,
+ const char *md5_salt, int md5_salt_len,
+ char **logdetail)
+{
+ int retval;
+ char crypt_pwd[MD5_PASSWD_LEN + 1];
+ char crypt_pwd2[MD5_PASSWD_LEN + 1];
+
+ Assert(md5_salt_len > 0);
+
+ /*
+ * Compute the correct answer for the MD5 challenge.
+ *
+ * We do not bother setting logdetail for any pg_md5_encrypt failure
+ * 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))
{
- /* stored password is plain, double-encrypt */
- char *crypt_pwd2 = palloc(MD5_PASSWD_LEN + 1);
-
- if (!pg_md5_encrypt(shadow_pass,
- role,
- strlen(role),
- crypt_pwd2))
- {
- pfree(crypt_pwd);
- pfree(crypt_pwd2);
- return STATUS_ERROR;
- }
- if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
- md5_salt, md5_salt_len,
- crypt_pwd))
- {
- pfree(crypt_pwd);
- pfree(crypt_pwd2);
- return STATUS_ERROR;
- }
- pfree(crypt_pwd2);
+ return STATUS_ERROR;
}
}
else
{
- /* Client sent password in plaintext */
- if (isMD5(shadow_pass))
+ /* stored password is plain, double-encrypt */
+ if (!pg_md5_encrypt(shadow_pass,
+ role,
+ strlen(role),
+ crypt_pwd2))
{
- /* Encrypt user-supplied password to match stored MD5 */
- crypt_client_pass = palloc(MD5_PASSWD_LEN + 1);
- if (!pg_md5_encrypt(client_pass,
- role,
- strlen(role),
- crypt_client_pass))
- {
- pfree(crypt_client_pass);
- return STATUS_ERROR;
- }
+ return STATUS_ERROR;
+ }
+ if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
+ md5_salt, md5_salt_len,
+ crypt_pwd))
+ {
+ return STATUS_ERROR;
}
- crypt_pwd = shadow_pass;
}
- if (strcmp(crypt_client_pass, crypt_pwd) == 0)
+ if (strcmp(client_pass, crypt_pwd) == 0)
+ retval = STATUS_OK;
+ else
{
- /*
- * Password OK, now check to be sure we are not past rolvaliduntil
- */
- if (isnull)
- retval = STATUS_OK;
- else if (vuntil < GetCurrentTimestamp())
+ *logdetail = psprintf(_("Password does not match for user \"%s\"."),
+ role);
+ retval = STATUS_ERROR;
+ }
+
+ return retval;
+}
+
+/*
+ * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
+ *
+ * 'shadow_pass' is the user's correct password or password hash, as stored
+ * in pg_authid.rolpassword.
+ * 'client_pass' is the password given by the remote user.
+ *
+ * In the error case, optionally store a palloc'd string at *logdetail
+ * that will be sent to the postmaster log (but not the client).
+ */
+int
+plain_crypt_verify(const char *role, const char *shadow_pass,
+ const char *client_pass,
+ char **logdetail)
+{
+ int retval;
+ char crypt_client_pass[MD5_PASSWD_LEN + 1];
+
+ /*
+ * Client sent password in plaintext. If we have an MD5 hash stored, hash
+ * the password the client sent, and compare the hashes. Otherwise
+ * compare the plaintext passwords directly.
+ */
+ if (isMD5(shadow_pass))
+ {
+ if (!pg_md5_encrypt(client_pass,
+ role,
+ strlen(role),
+ crypt_client_pass))
{
- *logdetail = psprintf(_("User \"%s\" has an expired password."),
- role);
- retval = STATUS_ERROR;
+ /*
+ * 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;
}
- else
- retval = STATUS_OK;
+ client_pass = crypt_client_pass;
}
+
+ if (strcmp(client_pass, shadow_pass) == 0)
+ retval = STATUS_OK;
else
+ {
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
role);
-
- if (crypt_pwd != shadow_pass)
- pfree(crypt_pwd);
- if (crypt_client_pass != client_pass)
- pfree(crypt_client_pass);
+ retval = STATUS_ERROR;
+ }
return retval;
}