Add libpq warning message if the .pgpass-retrieved password fails.
authorBruce Momjian
Sat, 13 Mar 2010 14:55:57 +0000 (14:55 +0000)
committerBruce Momjian
Sat, 13 Mar 2010 14:55:57 +0000 (14:55 +0000)
Add ERRCODE_INVALID_PASSWORD sqlstate error code.

doc/src/sgml/errcodes.sgml
src/backend/libpq/auth.c
src/include/utils/errcodes.h
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/libpq-int.h
src/pl/plpgsql/src/plerrcodes.h

index 5819004f48a0af3f6f8f6d1ff23df816dc3f6852..b5962f98cd0e9beb32500d36ab8ad7309e99c126 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  <productname>PostgreSQL</productname> Error Codes
 invalid_authorization_specification
 
 
+
+28P01
+INVALID PASSWORD
+invalid_password
+
+
 
 
 Class 2B — Dependent Privilege Descriptors Still Exist
index 70b0f6656650f796c3eb5edb6883524742b20b83..8838113c575375ddcc7395735404ad7fa4a92071 100644 (file)
@@ -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 */
 }
index 6eee981bbdd7c40f28e4f432df9327630da3a8b4..f7be2611f863e929309b0b43a5f0b8e4ec45e691 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 /* 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')
index 1dc52ef14856a077b1e4982b50bcfb1f3cc69fd2..3e9af73cb0a19c2067599e099d2d9abe0d978808 100644 (file)
@@ -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
  *
index 56ee13dbf6d756e5261ebe7c4d518e61786c486e..6fe96ab1684bab7832d3c67bb57ec8b911f8165b 100644 (file)
@@ -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? */
 
index 34ba070d1f4177f15ba7f8f53813d974fde61312..99008be9bf273fd03ef06a7d4b2686e672cdca03 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
    "invalid_authorization_specification", ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION
 },
 
+{
+   "invalid_password", ERRCODE_INVALID_PASSWORD
+},
+
 {
    "dependent_privilege_descriptors_still_exist", ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST
 },