summaryrefslogtreecommitdiff
path: root/src/backend/libpq/auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r--src/backend/libpq/auth.c179
1 files changed, 115 insertions, 64 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index a699a09e9ad..5f4f55760c1 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -24,6 +24,7 @@
#include <sys/select.h>
#endif
+#include "commands/user.h"
#include "common/ip.h"
#include "common/md5.h"
#include "libpq/auth.h"
@@ -49,17 +50,15 @@ static char *recv_password_packet(Port *port);
/*----------------------------------------------------------------
- * MD5 authentication
+ * Password-based authentication methods (password, md5, and scram)
*----------------------------------------------------------------
*/
-static int CheckMD5Auth(Port *port, char **logdetail);
+static int CheckPasswordAuth(Port *port, char **logdetail);
+static int CheckPWChallengeAuth(Port *port, char **logdetail);
-/*----------------------------------------------------------------
- * Plaintext password authentication
- *----------------------------------------------------------------
- */
+static int CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail);
+static int CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail);
-static int CheckPasswordAuth(Port *port, char **logdetail);
/*----------------------------------------------------------------
* Ident authentication
@@ -200,12 +199,6 @@ static int CheckRADIUSAuth(Port *port);
static int PerformRadiusTransaction(char *server, char *secret, char *portstr, char *identifier, char *user_name, char *passwd);
-/*----------------------------------------------------------------
- * SASL authentication
- *----------------------------------------------------------------
- */
-static int CheckSASLAuth(Port *port, char **logdetail);
-
/*
* Maximum accepted size of GSS and SSPI authentication tokens.
*
@@ -291,7 +284,7 @@ auth_failed(Port *port, int status, char *logdetail)
break;
case uaPassword:
case uaMD5:
- case uaSASL:
+ case uaSCRAM:
errstr = gettext_noop("password authentication failed for user \"%s\"");
/* We use it to indicate if a .pgpass password failed. */
errcode_return = ERRCODE_INVALID_PASSWORD;
@@ -552,17 +545,14 @@ ClientAuthentication(Port *port)
break;
case uaMD5:
- status = CheckMD5Auth(port, &logdetail);
+ case uaSCRAM:
+ status = CheckPWChallengeAuth(port, &logdetail);
break;
case uaPassword:
status = CheckPasswordAuth(port, &logdetail);
break;
- case uaSASL:
- status = CheckSASLAuth(port, &logdetail);
- break;
-
case uaPAM:
#ifdef USE_PAM
status = CheckPAMAuth(port, port->user_name, "");
@@ -710,41 +700,34 @@ recv_password_packet(Port *port)
/*----------------------------------------------------------------
- * MD5 authentication
+ * Password-based authentication mechanisms
*----------------------------------------------------------------
*/
+/*
+ * Plaintext password authentication.
+ */
static int
-CheckMD5Auth(Port *port, char **logdetail)
+CheckPasswordAuth(Port *port, char **logdetail)
{
- char md5Salt[4]; /* Password salt */
char *passwd;
- char *shadow_pass;
int result;
+ char *shadow_pass;
- if (Db_user_namespace)
- ereport(FATAL,
- (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
- errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
-
- /* include the salt to use for computing the response */
- if (!pg_backend_random(md5Salt, 4))
- {
- ereport(LOG,
- (errmsg("could not generate random MD5 salt")));
- return STATUS_ERROR;
- }
-
- sendAuthRequest(port, AUTH_REQ_MD5, md5Salt, 4);
+ sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
passwd = recv_password_packet(port);
if (passwd == NULL)
return STATUS_EOF; /* client wouldn't send password */
- result = get_role_password(port->user_name, &shadow_pass, logdetail);
- if (result == STATUS_OK)
- result = md5_crypt_verify(port->user_name, shadow_pass, passwd,
- md5Salt, 4, logdetail);
+ shadow_pass = get_role_password(port->user_name, logdetail);
+ if (shadow_pass)
+ {
+ result = plain_crypt_verify(port->user_name, shadow_pass, passwd,
+ logdetail);
+ }
+ else
+ result = STATUS_ERROR;
if (shadow_pass)
pfree(shadow_pass);
@@ -753,42 +736,114 @@ CheckMD5Auth(Port *port, char **logdetail)
return result;
}
-/*----------------------------------------------------------------
- * Plaintext password authentication
- *----------------------------------------------------------------
+/*
+ * MD5 and SCRAM authentication.
*/
+static int
+CheckPWChallengeAuth(Port *port, char **logdetail)
+{
+ int auth_result;
+ char *shadow_pass;
+ PasswordType pwtype;
+
+ Assert(port->hba->auth_method == uaSCRAM ||
+ port->hba->auth_method == uaMD5);
+
+ /* First look up the user's password. */
+ shadow_pass = get_role_password(port->user_name, logdetail);
+
+ /*
+ * If the user does not exist, or has no password, we still go through the
+ * motions of authentication, to avoid revealing to the client that the
+ * user didn't exist. If 'md5' is allowed, we choose whether to use 'md5'
+ * or 'scram' authentication based on current password_encryption setting.
+ * The idea is that most genuine users probably have a password of that
+ * type, if we pretend that this user had a password of that type, too, it
+ * "blends in" best.
+ *
+ * If the user had a password, but it was expired, we'll use the details
+ * of the expired password for the authentication, but report it as
+ * failure to the client even if correct password was given.
+ */
+ if (!shadow_pass)
+ pwtype = Password_encryption;
+ else
+ pwtype = get_password_type(shadow_pass);
+
+ /*
+ * If 'md5' authentication is allowed, decide whether to perform 'md5' or
+ * 'scram' authentication based on the type of password the user has. If
+ * it's an MD5 hash, we must do MD5 authentication, and if it's a SCRAM
+ * verifier, we must do SCRAM authentication. If it's stored in
+ * plaintext, we could do either one, so we opt for the more secure
+ * mechanism, SCRAM.
+ *
+ * If MD5 authentication is not allowed, always use SCRAM. If the user
+ * had an MD5 password, CheckSCRAMAuth() will fail.
+ */
+ if (port->hba->auth_method == uaMD5 && pwtype == PASSWORD_TYPE_MD5)
+ {
+ auth_result = CheckMD5Auth(port, shadow_pass, logdetail);
+ }
+ else
+ {
+ auth_result = CheckSCRAMAuth(port, shadow_pass, logdetail);
+ }
+
+ if (shadow_pass)
+ pfree(shadow_pass);
+
+ /*
+ * If get_role_password() returned error, return error, even if the
+ * authentication succeeded.
+ */
+ if (!shadow_pass)
+ {
+ Assert(auth_result != STATUS_OK);
+ return STATUS_ERROR;
+ }
+ return auth_result;
+}
static int
-CheckPasswordAuth(Port *port, char **logdetail)
+CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail)
{
+ char md5Salt[4]; /* Password salt */
char *passwd;
int result;
- char *shadow_pass;
- sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
+ if (Db_user_namespace)
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+ errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
+
+ /* include the salt to use for computing the response */
+ if (!pg_backend_random(md5Salt, 4))
+ {
+ ereport(LOG,
+ (errmsg("could not generate random MD5 salt")));
+ return STATUS_ERROR;
+ }
+
+ sendAuthRequest(port, AUTH_REQ_MD5, md5Salt, 4);
passwd = recv_password_packet(port);
if (passwd == NULL)
return STATUS_EOF; /* client wouldn't send password */
- result = get_role_password(port->user_name, &shadow_pass, logdetail);
- if (result == STATUS_OK)
- result = plain_crypt_verify(port->user_name, shadow_pass, passwd,
- logdetail);
-
if (shadow_pass)
- pfree(shadow_pass);
+ result = md5_crypt_verify(port->user_name, shadow_pass, passwd,
+ md5Salt, 4, logdetail);
+ else
+ result = STATUS_ERROR;
+
pfree(passwd);
return result;
}
-/*----------------------------------------------------------------
- * SASL authentication system
- *----------------------------------------------------------------
- */
static int
-CheckSASLAuth(Port *port, char **logdetail)
+CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
{
int mtype;
StringInfoData buf;
@@ -796,8 +851,6 @@ CheckSASLAuth(Port *port, char **logdetail)
char *output = NULL;
int outputlen = 0;
int result;
- char *shadow_pass;
- bool doomed = false;
/*
* SASL auth is not supported for protocol versions before 3, because it
@@ -827,11 +880,9 @@ CheckSASLAuth(Port *port, char **logdetail)
* This is because we don't want to reveal to an attacker what usernames
* are valid, nor which users have a valid password.
*/
- if (get_role_password(port->user_name, &shadow_pass, logdetail) != STATUS_OK)
- doomed = true;
/* Initialize the status tracker for message exchanges */
- scram_opaq = pg_be_scram_init(port->user_name, shadow_pass, doomed);
+ scram_opaq = pg_be_scram_init(port->user_name, shadow_pass);
/*
* Loop through SASL message exchange. This exchange can consist of
@@ -875,7 +926,7 @@ CheckSASLAuth(Port *port, char **logdetail)
*/
result = pg_be_scram_exchange(scram_opaq, buf.data, buf.len,
&output, &outputlen,
- doomed ? NULL : logdetail);
+ logdetail);
/* input buffer no longer used */
pfree(buf.data);