]> granicus.if.org Git - postgresql/commitdiff
Log a detail message for auth failures due to missing or expired password.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 28 Jan 2014 02:04:09 +0000 (21:04 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 28 Jan 2014 02:04:09 +0000 (21:04 -0500)
It's worth distinguishing these cases from run-of-the-mill wrong-password
problems, since users have been known to waste lots of time pursuing the
wrong theory about what's failing.  Now, our longstanding policy about how
to report authentication failures is that we don't really want to tell the
*client* such things, since that might be giving information to a bad guy.
But there's nothing wrong with reporting the details to the postmaster log,
and indeed the comments in this area of the code contemplate that
interesting details should be so reported.  We just weren't handling these
particular interesting cases usefully.

To fix, add infrastructure allowing subroutines of ClientAuthentication()
to return a string to be added to the errdetail_log field of the main
authentication-failed error report.  We might later want to use this to
report other subcases of authentication failure the same way, but for the
moment I just dealt with password cases.

Per discussion of a patch from Josh Drake, though this is not what
he proposed.

src/backend/libpq/auth.c
src/backend/libpq/crypt.c
src/include/libpq/crypt.h

index 882dc8faf1b63161cdc353c31fb507a934eaeeb2..9d0c3893c8d565a7ed06f8aea31b6ad0ced95337 100644 (file)
@@ -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);
 
index 56b3ea8a21b0cba08998dec3b67114db67522e20..5451db6d97480c275d46b00c34452581ccb921b0 100644 (file)
 #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;
        }
index 818e57eaee77c53e0705bd7ded397972af2b762b..b91024f86cda9879ebcea52ef62ff95d8144b9e8 100644 (file)
@@ -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