Remove arbitrary restrictions on password length.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Sep 2020 00:09:18 +0000 (20:09 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Sep 2020 00:09:18 +0000 (20:09 -0400)
This patch started out with the goal of harmonizing various arbitrary
limits on password length, but after awhile a better idea emerged:
let's just get rid of those fixed limits.

recv_password_packet() has an arbitrary limit on the packet size,
which we don't really need, so just drop it.  (Note that this doesn't
really affect anything for MD5 or SCRAM password verification, since
those will hash the user's password to something shorter anyway.
It does matter for auth methods that require a cleartext password.)

Likewise remove the arbitrary error condition in pg_saslprep().

The remaining limits are mostly in client-side code that prompts
for passwords.  To improve those, refactor simple_prompt() so that
it allocates its own result buffer that can be made as big as
necessary.  Actually, it proves best to make a separate routine
pg_get_line() that has essentially the semantics of fgets(), except
that it allocates a suitable result buffer and hence will never
return a truncated line.  (pg_get_line has a lot of potential
applications to replace randomly-sized fgets buffers elsewhere,
but I'll leave that for another patch.)

I built pg_get_line() atop stringinfo.c, which requires moving
that code to src/common/; but that seems fine since it was a poor
fit for src/port/ anyway.

This patch is mostly mine, but it owes a good deal to Nathan Bossart
who pressed for a solution to the password length problem and
created a predecessor patch.  Also thanks to Peter Eisentraut and
Stephen Frost for ideas and discussion.

Discussion: https://postgr.es/m/09512C4F-8CB9-4021-B455-EF4C4F0D55A0@amazon.com

21 files changed:
contrib/oid2name/oid2name.c
contrib/vacuumlo/vacuumlo.c
src/backend/libpq/auth.c
src/bin/initdb/initdb.c
src/bin/pg_basebackup/streamutil.c
src/bin/pg_dump/pg_backup_db.c
src/bin/pg_dump/pg_dumpall.c
src/bin/pgbench/pgbench.c
src/bin/psql/command.c
src/bin/psql/startup.c
src/bin/scripts/common.c
src/bin/scripts/createuser.c
src/bin/scripts/dropuser.c
src/common/Makefile
src/common/pg_get_line.c [new file with mode: 0644]
src/common/saslprep.c
src/common/sprompt.c [moved from src/port/sprompt.c with 81% similarity]
src/include/common/string.h
src/include/port.h
src/port/Makefile
src/tools/msvc/Mkvcbuild.pm

index 91b7958c48efa0bef027688a4b5651fc62faf3ad..5a884e29049f8fbe14d877fb5582bb9f76f675b4 100644 (file)
@@ -12,6 +12,7 @@
 #include "catalog/pg_class_d.h"
 #include "common/connect.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "getopt_long.h"
 #include "libpq-fe.h"
 #include "pg_getopt.h"
@@ -293,8 +294,7 @@ PGconn *
 sql_conn(struct options *my_opts)
 {
    PGconn     *conn;
-   bool        have_password = false;
-   char        password[100];
+   char       *password = NULL;
    bool        new_pass;
    PGresult   *res;
 
@@ -316,7 +316,7 @@ sql_conn(struct options *my_opts)
        keywords[2] = "user";
        values[2] = my_opts->username;
        keywords[3] = "password";
-       values[3] = have_password ? password : NULL;
+       values[3] = password;
        keywords[4] = "dbname";
        values[4] = my_opts->dbname;
        keywords[5] = "fallback_application_name";
@@ -336,11 +336,10 @@ sql_conn(struct options *my_opts)
 
        if (PQstatus(conn) == CONNECTION_BAD &&
            PQconnectionNeedsPassword(conn) &&
-           !have_password)
+           !password)
        {
            PQfinish(conn);
-           simple_prompt("Password: ", password, sizeof(password), false);
-           have_password = true;
+           password = simple_prompt("Password: ", false);
            new_pass = true;
        }
    } while (new_pass);
index e4019fafaa9e7cc5d2ce1415d57089dd4b988082..532cc596c412834af67e5aa39a0b0c8c0d429280 100644 (file)
@@ -24,6 +24,7 @@
 #include "catalog/pg_class_d.h"
 #include "common/connect.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "getopt_long.h"
 #include "libpq-fe.h"
 #include "pg_getopt.h"
@@ -69,15 +70,11 @@ vacuumlo(const char *database, const struct _param *param)
    int         i;
    bool        new_pass;
    bool        success = true;
-   static bool have_password = false;
-   static char password[100];
+   static char *password = NULL;
 
    /* Note: password can be carried over from a previous call */
-   if (param->pg_prompt == TRI_YES && !have_password)
-   {
-       simple_prompt("Password: ", password, sizeof(password), false);
-       have_password = true;
-   }
+   if (param->pg_prompt == TRI_YES && !password)
+       password = simple_prompt("Password: ", false);
 
    /*
     * Start the connection.  Loop until we have a password if requested by
@@ -97,7 +94,7 @@ vacuumlo(const char *database, const struct _param *param)
        keywords[2] = "user";
        values[2] = param->pg_user;
        keywords[3] = "password";
-       values[3] = have_password ? password : NULL;
+       values[3] = password;
        keywords[4] = "dbname";
        values[4] = database;
        keywords[5] = "fallback_application_name";
@@ -115,12 +112,11 @@ vacuumlo(const char *database, const struct _param *param)
 
        if (PQstatus(conn) == CONNECTION_BAD &&
            PQconnectionNeedsPassword(conn) &&
-           !have_password &&
+           !password &&
            param->pg_prompt != TRI_NO)
        {
            PQfinish(conn);
-           simple_prompt("Password: ", password, sizeof(password), false);
-           have_password = true;
+           password = simple_prompt("Password: ", false);
            new_pass = true;
        }
    } while (new_pass);
index 02b6c3f127c6756116a725f595008b3edb9c1ebe..36565df4fc1e488dd7348aac9b3476b96a87bb0a 100644 (file)
@@ -698,7 +698,7 @@ recv_password_packet(Port *port)
    }
 
    initStringInfo(&buf);
-   if (pq_getmessage(&buf, 1000))  /* receive password */
+   if (pq_getmessage(&buf, 0)) /* receive password */
    {
        /* EOF - pq_getmessage already logged a suitable message */
        pfree(buf.data);
index 786672b1b6553703f0f09d54a60d12306a12c367..73ddf408654a8c0b7bb392bf1bb4bb4f6ac632be 100644 (file)
@@ -67,6 +67,7 @@
 #include "common/file_utils.h"
 #include "common/logging.h"
 #include "common/restricted_token.h"
+#include "common/string.h"
 #include "common/username.h"
 #include "fe_utils/string_utils.h"
 #include "getaddrinfo.h"
@@ -1481,23 +1482,25 @@ setup_auth(FILE *cmdfd)
 static void
 get_su_pwd(void)
 {
-   char        pwd1[100];
-   char        pwd2[100];
+   char       *pwd1;
 
    if (pwprompt)
    {
        /*
         * Read password from terminal
         */
+       char       *pwd2;
+
        printf("\n");
        fflush(stdout);
-       simple_prompt("Enter new superuser password: ", pwd1, sizeof(pwd1), false);
-       simple_prompt("Enter it again: ", pwd2, sizeof(pwd2), false);
+       pwd1 = simple_prompt("Enter new superuser password: ", false);
+       pwd2 = simple_prompt("Enter it again: ", false);
        if (strcmp(pwd1, pwd2) != 0)
        {
            fprintf(stderr, _("Passwords didn't match.\n"));
            exit(1);
        }
+       free(pwd2);
    }
    else
    {
@@ -1510,7 +1513,6 @@ get_su_pwd(void)
         * for now.
         */
        FILE       *pwf = fopen(pwfilename, "r");
-       int         i;
 
        if (!pwf)
        {
@@ -1518,7 +1520,8 @@ get_su_pwd(void)
                         pwfilename);
            exit(1);
        }
-       if (!fgets(pwd1, sizeof(pwd1), pwf))
+       pwd1 = pg_get_line(pwf);
+       if (!pwd1)
        {
            if (ferror(pwf))
                pg_log_error("could not read password from file \"%s\": %m",
@@ -1530,12 +1533,10 @@ get_su_pwd(void)
        }
        fclose(pwf);
 
-       i = strlen(pwd1);
-       while (i > 0 && (pwd1[i - 1] == '\r' || pwd1[i - 1] == '\n'))
-           pwd1[--i] = '\0';
+       (void) pg_strip_crlf(pwd1);
    }
 
-   superuser_password = pg_strdup(pwd1);
+   superuser_password = pwd1;
 }
 
 /*
index c08003e7f2c75e209bef60f7dd4fa4d961c2221e..be653ebb2d94a94f19bb6f51795718a73aa570c5 100644 (file)
@@ -22,6 +22,7 @@
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "datatype/timestamp.h"
 #include "port/pg_bswap.h"
 #include "pqexpbuffer.h"
@@ -49,8 +50,7 @@ char     *dbuser = NULL;
 char      *dbport = NULL;
 char      *dbname = NULL;
 int            dbgetpassword = 0;  /* 0=auto, -1=never, 1=always */
-static bool have_password = false;
-static char password[100];
+static char *password = NULL;
 PGconn    *conn = NULL;
 
 /*
@@ -150,20 +150,21 @@ GetConnection(void)
    }
 
    /* If -W was given, force prompt for password, but only the first time */
-   need_password = (dbgetpassword == 1 && !have_password);
+   need_password = (dbgetpassword == 1 && !password);
 
    do
    {
        /* Get a new password if appropriate */
        if (need_password)
        {
-           simple_prompt("Password: ", password, sizeof(password), false);
-           have_password = true;
+           if (password)
+               free(password);
+           password = simple_prompt("Password: ", false);
            need_password = false;
        }
 
        /* Use (or reuse, on a subsequent connection) password if we have it */
-       if (have_password)
+       if (password)
        {
            keywords[i] = "password";
            values[i] = password;
index 94af11b80a398834d1f9a651941c7b01fb412905..12899e26e292da5ba48b393334ef7b5f95c4bde2 100644 (file)
@@ -18,6 +18,7 @@
 #endif
 
 #include "common/connect.h"
+#include "common/string.h"
 #include "dumputils.h"
 #include "fe_utils/string_utils.h"
 #include "parallel.h"
@@ -122,7 +123,6 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
    const char *newdb;
    const char *newuser;
    char       *password;
-   char        passbuf[100];
    bool        new_pass;
 
    if (!reqdb)
@@ -141,10 +141,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
    password = AH->savedPassword;
 
    if (AH->promptPassword == TRI_YES && password == NULL)
-   {
-       simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
-       password = passbuf;
-   }
+       password = simple_prompt("Password: ", false);
 
    initPQExpBuffer(&connstr);
    appendPQExpBufferStr(&connstr, "dbname=");
@@ -191,8 +188,9 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
 
            if (AH->promptPassword != TRI_NO)
            {
-               simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
-               password = passbuf;
+               if (password && password != AH->savedPassword)
+                   free(password);
+               password = simple_prompt("Password: ", false);
            }
            else
                fatal("connection needs password");
@@ -201,6 +199,9 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
        }
    } while (new_pass);
 
+   if (password && password != AH->savedPassword)
+       free(password);
+
    /*
     * We want to remember connection's actual password, whether or not we got
     * it by prompting.  So we don't just store the password variable.
@@ -242,7 +243,6 @@ ConnectDatabase(Archive *AHX,
 {
    ArchiveHandle *AH = (ArchiveHandle *) AHX;
    char       *password;
-   char        passbuf[100];
    bool        new_pass;
 
    if (AH->connection)
@@ -251,10 +251,8 @@ ConnectDatabase(Archive *AHX,
    password = AH->savedPassword;
 
    if (prompt_password == TRI_YES && password == NULL)
-   {
-       simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
-       password = passbuf;
-   }
+       password = simple_prompt("Password: ", false);
+
    AH->promptPassword = prompt_password;
 
    /*
@@ -293,8 +291,7 @@ ConnectDatabase(Archive *AHX,
            prompt_password != TRI_NO)
        {
            PQfinish(AH->connection);
-           simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
-           password = passbuf;
+           password = simple_prompt("Password: ", false);
            new_pass = true;
        }
    } while (new_pass);
@@ -309,6 +306,9 @@ ConnectDatabase(Archive *AHX,
    PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,
                                        ALWAYS_SECURE_SEARCH_PATH_SQL));
 
+   if (password && password != AH->savedPassword)
+       free(password);
+
    /*
     * We want to remember connection's actual password, whether or not we got
     * it by prompting.  So we don't just store the password variable.
index 2c82b39af0d27bdae33eae3c336d7126ccf4e6c2..97d2b8dac1c6f57fc865458aa942f39baa16e400 100644 (file)
@@ -21,6 +21,7 @@
 #include "common/connect.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "dumputils.h"
 #include "fe_utils/string_utils.h"
 #include "getopt_long.h"
@@ -1643,14 +1644,10 @@ connectDatabase(const char *dbname, const char *connection_string,
    const char **keywords = NULL;
    const char **values = NULL;
    PQconninfoOption *conn_opts = NULL;
-   static bool have_password = false;
-   static char password[100];
+   static char *password = NULL;
 
-   if (prompt_password == TRI_YES && !have_password)
-   {
-       simple_prompt("Password: ", password, sizeof(password), false);
-       have_password = true;
-   }
+   if (prompt_password == TRI_YES && !password)
+       password = simple_prompt("Password: ", false);
 
    /*
     * Start the connection.  Loop until we have a password if requested by
@@ -1730,7 +1727,7 @@ connectDatabase(const char *dbname, const char *connection_string,
            values[i] = pguser;
            i++;
        }
-       if (have_password)
+       if (password)
        {
            keywords[i] = "password";
            values[i] = password;
@@ -1757,12 +1754,11 @@ connectDatabase(const char *dbname, const char *connection_string,
 
        if (PQstatus(conn) == CONNECTION_BAD &&
            PQconnectionNeedsPassword(conn) &&
-           !have_password &&
+           !password &&
            prompt_password != TRI_NO)
        {
            PQfinish(conn);
-           simple_prompt("Password: ", password, sizeof(password), false);
-           have_password = true;
+           password = simple_prompt("Password: ", false);
            new_pass = true;
        }
    } while (new_pass);
index 08a5947a9e66412848b06cfd16011ed6c4c19ff1..332eabf6379e69a91c762002891734dbeff9c35a 100644 (file)
@@ -59,6 +59,7 @@
 
 #include "common/int.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/cancel.h"
 #include "fe_utils/conditional.h"
 #include "getopt_long.h"
@@ -1174,8 +1175,7 @@ doConnect(void)
 {
    PGconn     *conn;
    bool        new_pass;
-   static bool have_password = false;
-   static char password[100];
+   static char *password = NULL;
 
    /*
     * Start the connection.  Loop until we have a password if requested by
@@ -1195,7 +1195,7 @@ doConnect(void)
        keywords[2] = "user";
        values[2] = login;
        keywords[3] = "password";
-       values[3] = have_password ? password : NULL;
+       values[3] = password;
        keywords[4] = "dbname";
        values[4] = dbName;
        keywords[5] = "fallback_application_name";
@@ -1215,11 +1215,10 @@ doConnect(void)
 
        if (PQstatus(conn) == CONNECTION_BAD &&
            PQconnectionNeedsPassword(conn) &&
-           !have_password)
+           !password)
        {
            PQfinish(conn);
-           simple_prompt("Password: ", password, sizeof(password), false);
-           have_password = true;
+           password = simple_prompt("Password: ", false);
            new_pass = true;
        }
    } while (new_pass);
index 9902a4a2ba8eb94f0c268880030d9200c56148cf..d4aa0976b5bf242664b14a7b659516451b0d8622 100644 (file)
@@ -26,6 +26,7 @@
 #include "command.h"
 #include "common.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "copy.h"
 #include "crosstabview.h"
 #include "describe.h"
@@ -1964,11 +1965,11 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
    {
        char       *opt0 = psql_scan_slash_option(scan_state,
                                                  OT_SQLID, NULL, true);
-       char        pw1[100];
-       char        pw2[100];
+       char       *pw1;
+       char       *pw2;
 
-       simple_prompt("Enter new password: ", pw1, sizeof(pw1), false);
-       simple_prompt("Enter it again: ", pw2, sizeof(pw2), false);
+       pw1 = simple_prompt("Enter new password: ", false);
+       pw2 = simple_prompt("Enter it again: ", false);
 
        if (strcmp(pw1, pw2) != 0)
        {
@@ -2013,6 +2014,8 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
 
        if (opt0)
            free(opt0);
+       free(pw1);
+       free(pw2);
    }
    else
        ignore_slash_options(scan_state);
@@ -2058,8 +2061,7 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
 
            if (!pset.inputfile)
            {
-               result = (char *) pg_malloc(4096);
-               simple_prompt(prompt_text, result, 4096, true);
+               result = simple_prompt(prompt_text, true);
            }
            else
            {
@@ -2982,19 +2984,19 @@ copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
 static char *
 prompt_for_password(const char *username)
 {
-   char        buf[100];
+   char       *result;
 
    if (username == NULL || username[0] == '\0')
-       simple_prompt("Password: ", buf, sizeof(buf), false);
+       result = simple_prompt("Password: ", false);
    else
    {
        char       *prompt_text;
 
        prompt_text = psprintf(_("Password for user %s: "), username);
-       simple_prompt(prompt_text, buf, sizeof(buf), false);
+       result = simple_prompt(prompt_text, false);
        free(prompt_text);
    }
-   return pg_strdup(buf);
+   return result;
 }
 
 static bool
index 3302bd4dd327cc1e9a69e60e92ee27245acef752..8232a0143bc9502d63d91dc656ca52df26bd0e1c 100644 (file)
@@ -17,6 +17,7 @@
 #include "command.h"
 #include "common.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "describe.h"
 #include "fe_utils/print.h"
 #include "getopt_long.h"
@@ -119,8 +120,7 @@ main(int argc, char *argv[])
 {
    struct adhoc_opts options;
    int         successResult;
-   bool        have_password = false;
-   char        password[100];
+   char       *password = NULL;
    bool        new_pass;
 
    pg_logging_init(argv[0]);
@@ -233,8 +233,7 @@ main(int argc, char *argv[])
         * offer a potentially wrong one.  Typical uses of this option are
         * noninteractive anyway.
         */
-       simple_prompt("Password: ", password, sizeof(password), false);
-       have_password = true;
+       password = simple_prompt("Password: ", false);
    }
 
    /* loop until we have a password if requested by backend */
@@ -251,7 +250,7 @@ main(int argc, char *argv[])
        keywords[2] = "user";
        values[2] = options.username;
        keywords[3] = "password";
-       values[3] = have_password ? password : NULL;
+       values[3] = password;
        keywords[4] = "dbname"; /* see do_connect() */
        values[4] = (options.list_dbs && options.dbname == NULL) ?
            "postgres" : options.dbname;
@@ -269,7 +268,7 @@ main(int argc, char *argv[])
 
        if (PQstatus(pset.db) == CONNECTION_BAD &&
            PQconnectionNeedsPassword(pset.db) &&
-           !have_password &&
+           !password &&
            pset.getPassword != TRI_NO)
        {
            /*
@@ -287,9 +286,8 @@ main(int argc, char *argv[])
                password_prompt = pg_strdup(_("Password: "));
            PQfinish(pset.db);
 
-           simple_prompt(password_prompt, password, sizeof(password), false);
+           password = simple_prompt(password_prompt, false);
            free(password_prompt);
-           have_password = true;
            new_pass = true;
        }
    } while (new_pass);
index 420d0d11a5a1595212fdf916374a51eb39d5dcc1..e987eef23434da6c85069905d1b7745ebda081ba 100644 (file)
@@ -20,6 +20,7 @@
 #include "common.h"
 #include "common/connect.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/cancel.h"
 #include "fe_utils/string_utils.h"
 
@@ -68,18 +69,17 @@ connectDatabase(const char *dbname, const char *pghost,
 {
    PGconn     *conn;
    bool        new_pass;
-   static bool have_password = false;
-   static char password[100];
+   static char *password = NULL;
 
-   if (!allow_password_reuse)
-       have_password = false;
-
-   if (!have_password && prompt_password == TRI_YES)
+   if (!allow_password_reuse && password)
    {
-       simple_prompt("Password: ", password, sizeof(password), false);
-       have_password = true;
+       free(password);
+       password = NULL;
    }
 
+   if (!password && prompt_password == TRI_YES)
+       password = simple_prompt("Password: ", false);
+
    /*
     * Start the connection.  Loop until we have a password if requested by
     * backend.
@@ -96,7 +96,7 @@ connectDatabase(const char *dbname, const char *pghost,
        keywords[2] = "user";
        values[2] = pguser;
        keywords[3] = "password";
-       values[3] = have_password ? password : NULL;
+       values[3] = password;
        keywords[4] = "dbname";
        values[4] = dbname;
        keywords[5] = "fallback_application_name";
@@ -122,8 +122,9 @@ connectDatabase(const char *dbname, const char *pghost,
            prompt_password != TRI_NO)
        {
            PQfinish(conn);
-           simple_prompt("Password: ", password, sizeof(password), false);
-           have_password = true;
+           if (password)
+               free(password);
+           password = simple_prompt("Password: ", false);
            new_pass = true;
        }
    } while (new_pass);
@@ -444,14 +445,21 @@ yesno_prompt(const char *question)
 
    for (;;)
    {
-       char        resp[10];
+       char       *resp;
 
-       simple_prompt(prompt, resp, sizeof(resp), true);
+       resp = simple_prompt(prompt, true);
 
        if (strcmp(resp, _(PG_YESLETTER)) == 0)
+       {
+           free(resp);
            return true;
+       }
        if (strcmp(resp, _(PG_NOLETTER)) == 0)
+       {
+           free(resp);
            return false;
+       }
+       free(resp);
 
        printf(_("Please answer \"%s\" or \"%s\".\n"),
               _(PG_YESLETTER), _(PG_NOLETTER));
index 9ced079ac7598f564f1f172eff9ef0b7cc39e15d..6179199563c413e5da6cfcc3b9632019cbe77102 100644 (file)
@@ -13,6 +13,7 @@
 #include "postgres_fe.h"
 #include "common.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/simple_list.h"
 #include "fe_utils/string_utils.h"
 
@@ -63,8 +64,6 @@ main(int argc, char *argv[])
    int         conn_limit = -2;    /* less than minimum valid value */
    bool        pwprompt = false;
    char       *newpassword = NULL;
-   char        newuser_buf[128];
-   char        newpassword_buf[100];
 
    /* Tri-valued variables.  */
    enum trivalue createdb = TRI_DEFAULT,
@@ -191,9 +190,7 @@ main(int argc, char *argv[])
    {
        if (interactive)
        {
-           simple_prompt("Enter name of role to add: ",
-                         newuser_buf, sizeof(newuser_buf), true);
-           newuser = newuser_buf;
+           newuser = simple_prompt("Enter name of role to add: ", true);
        }
        else
        {
@@ -206,17 +203,16 @@ main(int argc, char *argv[])
 
    if (pwprompt)
    {
-       char        pw2[100];
+       char       *pw2;
 
-       simple_prompt("Enter password for new role: ",
-                     newpassword_buf, sizeof(newpassword_buf), false);
-       simple_prompt("Enter it again: ", pw2, sizeof(pw2), false);
-       if (strcmp(newpassword_buf, pw2) != 0)
+       newpassword = simple_prompt("Enter password for new role: ", false);
+       pw2 = simple_prompt("Enter it again: ", false);
+       if (strcmp(newpassword, pw2) != 0)
        {
            fprintf(stderr, _("Passwords didn't match.\n"));
            exit(1);
        }
-       newpassword = newpassword_buf;
+       free(pw2);
    }
 
    if (superuser == 0)
index fee270d4f6d11ff0977fc2f9d25b9009131c8d4d..f7ddd1402db268e75f400ea0ecf597fb7d7e84dc 100644 (file)
@@ -13,6 +13,7 @@
 #include "postgres_fe.h"
 #include "common.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/string_utils.h"
 
 
@@ -47,7 +48,6 @@ main(int argc, char *argv[])
    enum trivalue prompt_password = TRI_DEFAULT;
    bool        echo = false;
    bool        interactive = false;
-   char        dropuser_buf[128];
 
    PQExpBufferData sql;
 
@@ -112,9 +112,7 @@ main(int argc, char *argv[])
    {
        if (interactive)
        {
-           simple_prompt("Enter name of role to drop: ",
-                         dropuser_buf, sizeof(dropuser_buf), true);
-           dropuser = dropuser_buf;
+           dropuser = simple_prompt("Enter name of role to drop: ", true);
        }
        else
        {
index 16619e4ba883619c878ae922cf0d2c307186e12a..ad8fd9e41c47adc820c3b3c92dfffbdbba1ed909 100644 (file)
@@ -63,6 +63,7 @@ OBJS_COMMON = \
    kwlookup.o \
    link-canary.o \
    md5.o \
+   pg_get_line.o \
    pg_lzcompress.o \
    pgfnames.o \
    psprintf.o \
@@ -92,7 +93,8 @@ OBJS_FRONTEND = \
    fe_memutils.o \
    file_utils.o \
    logging.o \
-   restricted_token.o
+   restricted_token.o \
+   sprompt.o
 
 # foo.o, foo_shlib.o, and foo_srv.o are all built from foo.c
 OBJS_SHLIB = $(OBJS_FRONTEND:%.o=%_shlib.o)
diff --git a/src/common/pg_get_line.c b/src/common/pg_get_line.c
new file mode 100644 (file)
index 0000000..3843367
--- /dev/null
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_get_line.c
+ *   fgets() with an expansible result buffer
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *   src/common/pg_get_line.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/string.h"
+#include "lib/stringinfo.h"
+
+
+/*
+ * pg_get_line()
+ *
+ * This is meant to be equivalent to fgets(), except that instead of
+ * reading into a caller-supplied, fixed-size buffer, it reads into
+ * a palloc'd (in frontend, really malloc'd) string, which is resized
+ * as needed to handle indefinitely long input lines.  The caller is
+ * responsible for pfree'ing the result string when appropriate.
+ *
+ * As with fgets(), returns NULL if there is a read error or if no
+ * characters are available before EOF.  The caller can distinguish
+ * these cases by checking ferror(stream).
+ *
+ * Since this is meant to be equivalent to fgets(), the trailing newline
+ * (if any) is not stripped.  Callers may wish to apply pg_strip_crlf().
+ *
+ * Note that while I/O errors are reflected back to the caller to be
+ * dealt with, an OOM condition for the palloc'd buffer will not be;
+ * there'll be an ereport(ERROR) or exit(1) inside stringinfo.c.
+ */
+char *
+pg_get_line(FILE *stream)
+{
+   StringInfoData buf;
+
+   initStringInfo(&buf);
+
+   /* Read some data, appending it to whatever we already have */
+   while (fgets(buf.data + buf.len, buf.maxlen - buf.len, stream) != NULL)
+   {
+       buf.len += strlen(buf.data + buf.len);
+
+       /* Done if we have collected a newline */
+       if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
+           return buf.data;
+
+       /* Make some more room in the buffer, and loop to read more data */
+       enlargeStringInfo(&buf, 128);
+   }
+
+   /* Did fgets() fail because of an I/O error? */
+   if (ferror(stream))
+   {
+       /* ensure that free() doesn't mess up errno */
+       int         save_errno = errno;
+
+       pfree(buf.data);
+       errno = save_errno;
+       return NULL;
+   }
+
+   /* If we read no data before reaching EOF, we should return NULL */
+   if (buf.len == 0)
+   {
+       pfree(buf.data);
+       return NULL;
+   }
+
+   /* No newline at EOF ... so return what we have */
+   return buf.data;
+}
index 2dedf6b0fb6830306693fab50581bab0cb904323..d60452f75f288472be5198d6544e4bb691b906f9 100644 (file)
 #include "common/unicode_norm.h"
 #include "mb/pg_wchar.h"
 
-/*
- * Limit on how large password's we will try to process.  A password
- * larger than this will be treated the same as out-of-memory.
- */
-#define MAX_PASSWORD_LENGTH        1024
-
 /*
  * In backend, we will use palloc/pfree.  In frontend, use malloc, and
  * return SASLPREP_OOM on out-of-memory.
@@ -1078,18 +1072,6 @@ pg_saslprep(const char *input, char **output)
    /* Ensure we return *output as NULL on failure */
    *output = NULL;
 
-   /* Check that the password isn't stupendously long */
-   if (strlen(input) > MAX_PASSWORD_LENGTH)
-   {
-#ifndef FRONTEND
-       ereport(ERROR,
-               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                errmsg("password too long")));
-#else
-       return SASLPREP_OOM;
-#endif
-   }
-
    /*
     * Quick check if the input is pure ASCII.  An ASCII string requires no
     * further processing.
similarity index 81%
rename from src/port/sprompt.c
rename to src/common/sprompt.c
index 6d8a8b2609ff0720046597019f68ca881dd066ea..0ec75da5bfe682dcfc1122eea91c2c6acba05219 100644 (file)
@@ -8,12 +8,15 @@
  *
  *
  * IDENTIFICATION
- *   src/port/sprompt.c
+ *   src/common/sprompt.c
  *
  *-------------------------------------------------------------------------
  */
 #include "c.h"
 
+#include "common/fe_memutils.h"
+#include "common/string.h"
+
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
  * passwords interactively.  Reads from /dev/tty or stdin/stderr.
  *
  * prompt:     The prompt to print, or NULL if none (automatically localized)
- * destination: buffer in which to store result
- * destlen:        allocated length of destination
  * echo:       Set to false if you want to hide what is entered (for passwords)
  *
- * The input (without trailing newline) is returned in the destination buffer,
- * with a '\0' appended.
+ * The input (without trailing newline) is returned as a malloc'd string.
+ * Caller is responsible for freeing it when done.
  */
-void
-simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
+char *
+simple_prompt(const char *prompt, bool echo)
 {
-   int         length;
+   char       *result;
    FILE       *termin,
               *termout;
-
 #if defined(HAVE_TERMIOS_H)
    struct termios t_orig,
                t;
@@ -126,29 +126,14 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
        fflush(termout);
    }
 
-   if (fgets(destination, destlen, termin) == NULL)
-       destination[0] = '\0';
+   result = pg_get_line(termin);
 
-   length = strlen(destination);
-   if (length > 0 && destination[length - 1] != '\n')
-   {
-       /* eat rest of the line */
-       char        buf[128];
-       int         buflen;
-
-       do
-       {
-           if (fgets(buf, sizeof(buf), termin) == NULL)
-               break;
-           buflen = strlen(buf);
-       } while (buflen > 0 && buf[buflen - 1] != '\n');
-   }
+   /* If we failed to read anything, just return an empty string */
+   if (result == NULL)
+       result = pg_strdup("");
 
    /* strip trailing newline, including \r in case we're on Windows */
-   while (length > 0 &&
-          (destination[length - 1] == '\n' ||
-           destination[length - 1] == '\r'))
-       destination[--length] = '\0';
+   (void) pg_strip_crlf(result);
 
    if (!echo)
    {
@@ -169,4 +154,6 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
        fclose(termin);
        fclose(termout);
    }
+
+   return result;
 }
index 5113c04434c8d02f240f609eb4a7217ed6ce493e..18aa1dc5aa5c24545e2a86401e3e5cf4cbe76fc5 100644 (file)
 #ifndef COMMON_STRING_H
 #define COMMON_STRING_H
 
+/* functions in src/common/string.c */
 extern bool pg_str_endswith(const char *str, const char *end);
 extern int strtoint(const char *pg_restrict str, char **pg_restrict endptr,
                     int base);
 extern void pg_clean_ascii(char *str);
 extern int pg_strip_crlf(char *str);
 
+/* functions in src/common/pg_get_line.c */
+extern char *pg_get_line(FILE *stream);
+
+/* functions in src/common/sprompt.c */
+extern char *simple_prompt(const char *prompt, bool echo);
+
 #endif                         /* COMMON_STRING_H */
index 271ff0d00bcce5ec2193060ba01d0a8f7704f9af..84bf2c363f55afa4a2f2f76e5cbd0a20b5f5f33a 100644 (file)
@@ -213,10 +213,6 @@ extern char *pg_strerror_r(int errnum, char *buf, size_t buflen);
 /* Wrap strsignal(), or provide our own version if necessary */
 extern const char *pg_strsignal(int signum);
 
-/* Portable prompt handling */
-extern void simple_prompt(const char *prompt, char *destination, size_t destlen,
-                         bool echo);
-
 extern int pclose_check(FILE *stream);
 
 /* Global variable holding time zone information. */
index 8defa1257bb8a8ea106fea1975e1e2493dfad7e0..e41b005c4f1bf9cd77714f608d1d351483f905fa 100644 (file)
@@ -35,6 +35,8 @@ include $(top_builddir)/src/Makefile.global
 override CPPFLAGS := -I$(top_builddir)/src/port -DFRONTEND $(CPPFLAGS)
 LIBS += $(PTHREAD_LIBS)
 
+# If you add objects here, see also src/tools/msvc/Mkvcbuild.pm
+
 OBJS = \
    $(LIBOBJS) \
    $(PG_CRC32C_OBJS) \
@@ -55,7 +57,6 @@ OBJS = \
    qsort_arg.o \
    quotes.o \
    snprintf.o \
-   sprompt.o \
    strerror.o \
    tar.o \
    thread.o
index 20da7985c10114f462d7ae0240e0bb90f0cfdc12..536df5a92e75ff02ccd4874c68af402c44e5bb38 100644 (file)
@@ -102,7 +102,7 @@ sub mkvcbuild
      pread.c pwrite.c pg_bitutils.c
      pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
      pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
-     sprompt.c strerror.c tar.c thread.c
+     strerror.c tar.c thread.c
      win32env.c win32error.c win32security.c win32setlocale.c);
 
    push(@pgportfiles, 'strtof.c') if ($vsVersion < '14.00');
@@ -123,7 +123,7 @@ sub mkvcbuild
      config_info.c controldata_utils.c d2s.c encnames.c exec.c
      f2s.c file_perm.c hashfn.c ip.c jsonapi.c
      keywords.c kwlookup.c link-canary.c md5.c
-     pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
+     pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
      saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
      wait_error.c wchar.c);
 
@@ -139,7 +139,7 @@ sub mkvcbuild
 
    our @pgcommonfrontendfiles = (
        @pgcommonallfiles, qw(fe_memutils.c file_utils.c
-         logging.c restricted_token.c));
+         logging.c restricted_token.c sprompt.c));
 
    our @pgcommonbkndfiles = @pgcommonallfiles;