From a6c1cea2b7ac446558ce0cde14b19e74220eeb7f Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Sat, 13 Mar 2010 14:55:57 +0000
Subject: [PATCH] Add libpq warning message if the .pgpass-retrieved password
 fails.

Add ERRCODE_INVALID_PASSWORD sqlstate error code.
---
 doc/src/sgml/errcodes.sgml        |  8 +++-
 src/backend/libpq/auth.c          |  9 ++--
 src/include/utils/errcodes.h      |  3 +-
 src/interfaces/libpq/fe-connect.c | 71 +++++++++++++++++++++++++------
 src/interfaces/libpq/libpq-int.h  |  3 +-
 src/pl/plpgsql/src/plerrcodes.h   |  6 ++-
 6 files changed, 80 insertions(+), 20 deletions(-)

diff --git a/doc/src/sgml/errcodes.sgml b/doc/src/sgml/errcodes.sgml
index 5819004f48a..b5962f98cd0 100644
--- a/doc/src/sgml/errcodes.sgml
+++ b/doc/src/sgml/errcodes.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/errcodes.sgml,v 1.28 2009/12/07 05:22:21 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/errcodes.sgml,v 1.29 2010/03/13 14:55:57 momjian Exp $ -->
 
 <appendix id="errcodes-appendix">
  <title><productname>PostgreSQL</productname> Error Codes</title>
@@ -761,6 +761,12 @@
 <entry>invalid_authorization_specification</entry>
 </row>
 
+<row>
+<entry><literal>28P01</literal></entry>
+<entry>INVALID PASSWORD</entry>
+<entry>invalid_password</entry>
+</row>
+
 
 <row>
 <entry spanname="span13"><emphasis role="bold">Class 2B &mdash; Dependent Privilege Descriptors Still Exist</></entry>
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 70b0f665665..8838113c575 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.195 2010/02/26 02:00:42 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.196 2010/03/13 14:55:57 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -232,7 +232,8 @@ static void
 auth_failed(Port *port, int status)
 {
 	const char *errstr;
-
+	int		errcode_return = ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION;
+	
 	/*
 	 * If we failed due to EOF from client, just quit; there's no point in
 	 * trying to send a message to the client, and not much point in logging
@@ -269,6 +270,8 @@ auth_failed(Port *port, int status)
 		case uaMD5:
 		case uaPassword:
 			errstr = gettext_noop("password authentication failed for user \"%s\"");
+			/* We use it to indicate if a .pgpass password failed. */
+			errcode_return = ERRCODE_INVALID_PASSWORD;
 			break;
 		case uaPAM:
 			errstr = gettext_noop("PAM authentication failed for user \"%s\"");
@@ -285,7 +288,7 @@ auth_failed(Port *port, int status)
 	}
 
 	ereport(FATAL,
-			(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+			(errcode(errcode_return),
 			 errmsg(errstr, port->user_name)));
 	/* doesn't return */
 }
diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h
index 6eee981bbdd..f7be2611f86 100644
--- a/src/include/utils/errcodes.h
+++ b/src/include/utils/errcodes.h
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 2003-2010, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.31 2010/01/02 16:58:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.32 2010/03/13 14:55:57 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,6 +194,7 @@
 
 /* Class 28 - Invalid Authorization Specification */
 #define ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION MAKE_SQLSTATE('2','8', '0','0','0')
+#define ERRCODE_INVALID_PASSWORD MAKE_SQLSTATE('2','8', 'P','0','1')
 
 /* Class 2B - Dependent Privilege Descriptors Still Exist */
 #define ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST		MAKE_SQLSTATE('2','B', '0','0','0')
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 1dc52ef1485..3e9af73cb0a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.389 2010/03/03 20:31:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.390 2010/03/13 14:55:57 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,6 +91,9 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
  */
 #define ERRCODE_APPNAME_UNKNOWN "42704"
 
+/* This is part of the protocol so just define it */
+#define ERRCODE_INVALID_PASSWORD "28P01"
+
 /*
  * fall back options if they are not specified by arguments or defined
  * by environment variables
@@ -284,6 +287,8 @@ static int parseServiceFile(const char *serviceFile,
 static char *pwdfMatchesString(char *buf, char *token);
 static char *PasswordFromFile(char *hostname, char *port, char *dbname,
 				 char *username);
+static bool getPgPassFilename(char *pgpassfile);
+static void dot_pg_pass_warning(PGconn *conn);
 static void default_threadlock(int acquire);
 
 
@@ -652,6 +657,8 @@ connectOptions2(PGconn *conn)
 										conn->dbName, conn->pguser);
 		if (conn->pgpass == NULL)
 			conn->pgpass = strdup(DefaultPassword);
+		else
+			conn->dot_pgpass_used = true;
 	}
 
 	/*
@@ -2133,6 +2140,8 @@ keep_going:						/* We will come back to here until there is
 
 error_return:
 
+	dot_pg_pass_warning(conn);
+	
 	/*
 	 * We used to close the socket at this point, but that makes it awkward
 	 * for those above us if they wish to remove this socket from their own
@@ -2191,6 +2200,7 @@ makeEmptyPGconn(void)
 	conn->verbosity = PQERRORS_DEFAULT;
 	conn->sock = -1;
 	conn->password_needed = false;
+	conn->dot_pgpass_used = false;
 #ifdef USE_SSL
 	conn->allow_ssl_try = true;
 	conn->wait_ssl_try = false;
@@ -4323,7 +4333,6 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
 	FILE	   *fp;
 	char		pgpassfile[MAXPGPATH];
 	struct stat stat_buf;
-	char	   *passfile_env;
 
 #define LINELEN NAMEDATALEN*5
 	char		buf[LINELEN];
@@ -4349,17 +4358,8 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
 	if (port == NULL)
 		port = DEF_PGPORT_STR;
 
-	if ((passfile_env = getenv("PGPASSFILE")) != NULL)
-		/* use the literal path from the environment, if set */
-		strlcpy(pgpassfile, passfile_env, sizeof(pgpassfile));
-	else
-	{
-		char		homedir[MAXPGPATH];
-
-		if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
-			return NULL;
-		snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE);
-	}
+	if (!getPgPassFilename(pgpassfile))
+		return NULL;
 
 	/* If password file cannot be opened, ignore it. */
 	if (stat(pgpassfile, &stat_buf) != 0)
@@ -4426,6 +4426,51 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
 #undef LINELEN
 }
 
+
+static bool getPgPassFilename(char *pgpassfile)
+{
+	char	   *passfile_env;
+
+	if ((passfile_env = getenv("PGPASSFILE")) != NULL)
+		/* use the literal path from the environment, if set */
+		strlcpy(pgpassfile, passfile_env, MAXPGPATH);
+	else
+	{
+		char		homedir[MAXPGPATH];
+
+		if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
+			return false;
+		snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE);
+	}
+	return true;
+}
+
+/*
+ *	If the connection failed, we should mention if
+ *	we got the password from .pgpass in case that
+ *	password is wrong.
+ */
+static void
+dot_pg_pass_warning(PGconn *conn)
+{
+	/* If it was 'invalid authorization', add .pgpass mention */
+	if (conn->dot_pgpass_used && conn->password_needed && conn->result &&
+		/* only works with >= 9.0 servers */
+		strcmp(PQresultErrorField(conn->result, PG_DIAG_SQLSTATE),
+			ERRCODE_INVALID_PASSWORD) == 0)
+	{
+		char		pgpassfile[MAXPGPATH];
+
+		if (!getPgPassFilename(pgpassfile))
+			return;
+		appendPQExpBufferStr(&conn->errorMessage,
+			libpq_gettext("password retrieved from "));
+		appendPQExpBufferStr(&conn->errorMessage, pgpassfile);
+		appendPQExpBufferChar(&conn->errorMessage, '\n');
+	}
+}
+
+	
 /*
  * Obtain user's home directory, return in given buffer
  *
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 56ee13dbf6d..6fe96ab1684 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.149 2010/02/26 02:01:33 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.150 2010/03/13 14:55:57 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -343,6 +343,7 @@ struct pg_conn
 	ProtocolVersion pversion;	/* FE/BE protocol version in use */
 	int			sversion;		/* server version, e.g. 70401 for 7.4.1 */
 	bool		password_needed;	/* true if server demanded a password */
+	bool		dot_pgpass_used;	/* true if used .pgpass */
 	bool		sigpipe_so;		/* have we masked SIGPIPE via SO_NOSIGPIPE? */
 	bool		sigpipe_flag;	/* can we mask SIGPIPE via MSG_NOSIGNAL? */
 
diff --git a/src/pl/plpgsql/src/plerrcodes.h b/src/pl/plpgsql/src/plerrcodes.h
index 34ba070d1f4..99008be9bf2 100644
--- a/src/pl/plpgsql/src/plerrcodes.h
+++ b/src/pl/plpgsql/src/plerrcodes.h
@@ -9,7 +9,7 @@
  *
  * Copyright (c) 2003-2010, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.20 2010/01/02 16:58:13 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.21 2010/03/13 14:55:57 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -367,6 +367,10 @@
 	"invalid_authorization_specification", ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION
 },
 
+{
+	"invalid_password", ERRCODE_INVALID_PASSWORD
+},
+
 {
 	"dependent_privilege_descriptors_still_exist", ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST
 },
-- 
GitLab