diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 882dc8faf1..9d0c3893c8 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -38,9 +38,9 @@ *---------------------------------------------------------------- */ static void sendAuthRequest(Port *port, AuthRequest areq); -static void auth_failed(Port *port, int status); +static void auth_failed(Port *port, int status, char *logdetail); static char *recv_password_packet(Port *port); -static int recv_and_check_password_packet(Port *port); +static int recv_and_check_password_packet(Port *port, char **logdetail); /*---------------------------------------------------------------- @@ -207,10 +207,11 @@ ClientAuthentication_hook_type ClientAuthentication_hook = NULL; * in use, and these are items that must be presumed known to an attacker * anyway. * Note that many sorts of failure report additional information in the - * postmaster log, which we hope is only readable by good guys. + * postmaster log, which we hope is only readable by good guys. In + * particular, if logdetail isn't NULL, we send that string to the log. */ static void -auth_failed(Port *port, int status) +auth_failed(Port *port, int status, char *logdetail) { const char *errstr; int errcode_return = ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION; @@ -273,14 +274,21 @@ auth_failed(Port *port, int status) } if (port->hba) - ereport(FATAL, - (errcode(errcode_return), - errmsg(errstr, port->user_name), - errdetail_log("Connection matched pg_hba.conf line %d: \"%s\"", port->hba->linenumber, port->hba->rawline))); - else - ereport(FATAL, - (errcode(errcode_return), - errmsg(errstr, port->user_name))); + { + char *cdetail; + + cdetail = psprintf(_("Connection matched pg_hba.conf line %d: \"%s\""), + port->hba->linenumber, port->hba->rawline); + if (logdetail) + logdetail = psprintf("%s\n%s", logdetail, cdetail); + else + logdetail = cdetail; + } + + ereport(FATAL, + (errcode(errcode_return), + errmsg(errstr, port->user_name), + logdetail ? errdetail_log("%s", logdetail) : 0)); /* doesn't return */ } @@ -294,6 +302,7 @@ void ClientAuthentication(Port *port) { int status = STATUS_ERROR; + char *logdetail = NULL; /* * Get the authentication method to use for this frontend/database @@ -507,12 +516,12 @@ ClientAuthentication(Port *port) (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"))); sendAuthRequest(port, AUTH_REQ_MD5); - status = recv_and_check_password_packet(port); + status = recv_and_check_password_packet(port, &logdetail); break; case uaPassword: sendAuthRequest(port, AUTH_REQ_PASSWORD); - status = recv_and_check_password_packet(port); + status = recv_and_check_password_packet(port, &logdetail); break; case uaPAM: @@ -552,7 +561,7 @@ ClientAuthentication(Port *port) if (status == STATUS_OK) sendAuthRequest(port, AUTH_REQ_OK); else - auth_failed(port, status); + auth_failed(port, status, logdetail); /* Done with authentication, so we should turn off immediate interrupts */ ImmediateInterruptOK = false; @@ -680,9 +689,10 @@ recv_password_packet(Port *port) /* * Called when we have sent an authorization request for a password. * Get the response and check it. + * On error, optionally store a detail string at *logdetail. */ static int -recv_and_check_password_packet(Port *port) +recv_and_check_password_packet(Port *port, char **logdetail) { char *passwd; int result; @@ -692,7 +702,7 @@ recv_and_check_password_packet(Port *port) if (passwd == NULL) return STATUS_EOF; /* client wouldn't send password */ - result = md5_crypt_verify(port, port->user_name, passwd); + result = md5_crypt_verify(port, port->user_name, passwd, logdetail); pfree(passwd); diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 56b3ea8a21..5451db6d97 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -29,8 +29,14 @@ #include "utils/timestamp.h" +/* + * Check given password for given user, and return STATUS_OK or STATUS_ERROR. + * 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 Port *port, const char *role, char *client_pass) +md5_crypt_verify(const Port *port, const char *role, char *client_pass, + char **logdetail) { int retval = STATUS_ERROR; char *shadow_pass, @@ -58,6 +64,8 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass) if (isnull) { ReleaseSysCache(roleTup); + *logdetail = psprintf(_("User \"%s\" has no password assigned."), + role); return STATUS_ERROR; /* user has no password */ } shadow_pass = TextDatumGetCString(datum); @@ -148,7 +156,11 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass) if (isnull) retval = STATUS_OK; else if (vuntil < GetCurrentTimestamp()) + { + *logdetail = psprintf(_("User \"%s\" has an expired password."), + role); retval = STATUS_ERROR; + } else retval = STATUS_OK; } diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 818e57eaee..b91024f86c 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -15,7 +15,7 @@ #include "libpq/libpq-be.h" -extern int md5_crypt_verify(const Port *port, const char *user, - char *client_pass); +extern int md5_crypt_verify(const Port *port, const char *role, + char *client_pass, char **logdetail); #endif