Thread: Maximum password length

Maximum password length

From
"Bossart, Nathan"
Date:
Hello,

I recently noticed a few restrictions on exceptionally long passwords
that don't seem to be documented.  While scram-sha-256 has a limit of
1,024 characters [0], other password-based authentication mechanisms
do not seem to have a well-defined limit.  Furthermore, there is a
1,000 character restriction on password messages [1], which limits the
effective maximum length of the content of the message to 995
characters (due to the '\0' byte and 4 bytes for the length of the
message).  This 995 character restriction shouldn't impact md5 or
scram-sha-256 authentication, but it will impact "password"
authentication.  On top of all this, client utilities truncate
passwords provided via prompt to 99 characters, so longer passwords
must be provided via alternatives such as .pgpass and PGPASSWORD.

I suspect these limits are acceptable for the vast majority of users,
but it is presumably very confusing to users who attempt to use longer
passwords.  For example, the truncation performed by client utilities
like psql is done silently, specifying a scram-sha-256 password that
is too long will result in a "password too long" message, and
providing a password message longer than 995 characters will result in
a "server closed the connection" error and an "invalid message length"
log statement.

I've attached 2 patches in an effort to clarify the upper bounds on
password lengths:
    - 0001 refactors the hard-coded 100 character buffer size used for
      password prompts for client utilities into a
      PROMPT_MAX_PASSWORD_LENGTH macro in postgres_fe.h.
    - 0002 is an attempt at documenting the password length
      restrictions and suggested workarounds for longer passwords.

I've also attached a third patch that increases the maximum length of
password messages accepted by the server to 8,192 characters.  The
current limit of 1,000 characters can be insufficient for very long
passwords provided via "password" authentication.  IMO this server
message limit is especially confusing for scram-sha-256 passwords, as
they can be up to 1,024 characters long, but with "password"
authentication, only 995 characters can be used to connect to the
server.  Other forms of authentication similar to "password" (LDAP,
RADIUS, PAM, BSD) are likewise impacted by the server message limit
and may benefit from this increase.

I am submitting these patches for consideration in commitfest 2018-11.

Nathan

[0]
https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/common/saslprep.c;h=4cf574fed87ad830bcf8fdb105e37f8b4df0ee44;hb=HEAD#l42

[1]
https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/libpq/auth.c;h=85175655359829a2cf50dd883066bbb3d45e2286;hb=HEAD#l682


Attachment

Re: Maximum password length

From
Stephen Frost
Date:
Greetings,

* Bossart, Nathan (bossartn@amazon.com) wrote:
> I've attached 2 patches in an effort to clarify the upper bounds on
> password lengths:
>     - 0001 refactors the hard-coded 100 character buffer size used for
>       password prompts for client utilities into a
>       PROMPT_MAX_PASSWORD_LENGTH macro in postgres_fe.h.
>     - 0002 is an attempt at documenting the password length
>       restrictions and suggested workarounds for longer passwords.

If we're going to do work in this area, why wouldn't we have the client
tools and the server agree on the max length and then have them all be
consistent..?

Seems odd to decide that 100 character buffer size in the clients makes
sense and then make the server support an 8k password.

I'm also trying to figure out why it makes sense to support an 8k
password and if we've really tried seeing what happens if pg_authid gets
a toast table that's actually used for passwords...

I'll note your patches neglected to include any tests...

Thanks!

Stephen

Attachment

Re: Maximum password length

From
Isaac Morland
Date:
On Fri, 12 Oct 2018 at 16:52, Stephen Frost <sfrost@snowman.net> wrote:
 
I'm also trying to figure out why it makes sense to support an 8k
password and if we've really tried seeing what happens if pg_authid gets
a toast table that's actually used for passwords...

pg_authid.rolpassword stores a hash, so the password length does not affect it.

Of course, this also means that even in principle super-long passwords don't increase security, since one "can" (again, in principle) brute-force any password by guessing the first not-very-many-more-than-the-total-number-of-distinct-hashes possible passwords, starting with the shortest passwords and working up to longer passwords.

It's also obvious that past a certain point, longer passwords don't help anyway, because it's already enough to have a password that can't be guessed in, say, the expected duration of the Earth's existence using all the computing power currently available in the world.

I agree there should be a specific limit that is the same in libpq, on the server, and in the protocol. Maybe 128 characters, to get a nice round number? This is still way longer than the 32-byte SHA 256 hash. Or 64, which is still plenty but doesn't involve extending the current character buffer size to a longer value while still hugely exceeding the amount of information in the hash.

Re: Maximum password length

From
"Bossart, Nathan"
Date:
Hi Stephen,

On 10/12/18, 3:52 PM, "Stephen Frost" <sfrost@snowman.net> wrote:
> If we're going to do work in this area, why wouldn't we have the client
> tools and the server agree on the max length and then have them all be
> consistent..?
> 
> Seems odd to decide that 100 character buffer size in the clients makes
> sense and then make the server support an 8k password.

I considered this but wondered if expanding the buffers over 80x was
too intrusive or if the 100 character limit had some historical
purpose.  I'm happy to align everything if desired.

> I'm also trying to figure out why it makes sense to support an 8k
> password and if we've really tried seeing what happens if pg_authid gets
> a toast table that's actually used for passwords...

Since v10+ always stores passwords encrypted [0], I don't think it
will require a TOAST table.

> I'll note your patches neglected to include any tests...

I will look into adding tests.  I've also been told that there may be
some limits for the .pgpass file, so I am looking into that as well.

Nathan

[0] https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=eb61136dc75a76caef8460fa939244d8593100f2


Re: Maximum password length

From
Stephen Frost
Date:
Greetings,

* Isaac Morland (isaac.morland@gmail.com) wrote:
> On Fri, 12 Oct 2018 at 16:52, Stephen Frost <sfrost@snowman.net> wrote:
> > I'm also trying to figure out why it makes sense to support an 8k
> > password and if we've really tried seeing what happens if pg_authid gets
> > a toast table that's actually used for passwords...
>
> pg_authid.rolpassword stores a hash, so the password length does not affect
> it.

I had been thinking about storing of plaintext passwords, which we
certainly used to do, but forgot that we actually did remove that,
finally, so this specific point isn't a concern any longer, though of
course the rest is.

> Of course, this also means that even in principle super-long passwords
> don't increase security, since one "can" (again, in principle) brute-force
> any password by guessing the first
> not-very-many-more-than-the-total-number-of-distinct-hashes possible
> passwords, starting with the shortest passwords and working up to longer
> passwords.

Well, as you say, length doesn't matter here, if all you're doing is
enumerating all possible responses to the server.

> It's also obvious that past a certain point, longer passwords don't help
> anyway, because it's already enough to have a password that can't be
> guessed in, say, the expected duration of the Earth's existence using all
> the computing power currently available in the world.

Not sure I really am all that keen to get into that debate. :)

> I agree there should be a specific limit that is the same in libpq, on the
> server, and in the protocol. Maybe 128 characters, to get a nice round
> number? This is still way longer than the 32-byte SHA 256 hash. Or 64,
> which is still plenty but doesn't involve extending the current character
> buffer size to a longer value while still hugely exceeding the amount of
> information in the hash.

I certainly don't think that we should break things which do work today,
which would include long plaintext passwords sent by clients.

Even if our clients don't support >100 character passwords, if the
server does, then someone might be using one.

Thanks!

Stephen

Attachment

Re: Maximum password length

From
"Bossart, Nathan"
Date:
Hi Isaac,

On 10/12/18, 4:04 PM, "Isaac Morland" <isaac.morland@gmail.com> wrote:
> I agree there should be a specific limit that is the same in libpq,
> on the server, and in the protocol. Maybe 128 characters, to get a
> nice round number? This is still way longer than the 32-byte SHA 256
> hash. Or 64, which is still plenty but doesn't involve extending the
> current character buffer size to a longer value while still hugely
> exceeding the amount of information in the hash.

My main motivation for suggesting the increase to 8k is to provide
flexibility for alternative authentication methods like LDAP, RADIUS,
PAM, and BSD.

Nathan


Re: Maximum password length

From
Stephen Frost
Date:
Greetings,

* Bossart, Nathan (bossartn@amazon.com) wrote:
> On 10/12/18, 3:52 PM, "Stephen Frost" <sfrost@snowman.net> wrote:
> > If we're going to do work in this area, why wouldn't we have the client
> > tools and the server agree on the max length and then have them all be
> > consistent..?
> >
> > Seems odd to decide that 100 character buffer size in the clients makes
> > sense and then make the server support an 8k password.
>
> I considered this but wondered if expanding the buffers over 80x was
> too intrusive or if the 100 character limit had some historical
> purpose.  I'm happy to align everything if desired.

The way to sort that out would likely to be go look at the history...

That said, assuming we do adjust the limit to be higher, it'd probably
make more sense to allocate it and not just have it on the stack (which
might be why it's the size it is today...).

> > I'm also trying to figure out why it makes sense to support an 8k
> > password and if we've really tried seeing what happens if pg_authid gets
> > a toast table that's actually used for passwords...
>
> Since v10+ always stores passwords encrypted [0], I don't think it
> will require a TOAST table.

Yeah, that was pointed out downthread, I'd forgotten that we (finally)
got rid of storing plaintext passwords; sometimes it's difficult to
believe that we've actually moved forward with something that some of us
complained about many, many, many years ago. ;)

> > I'll note your patches neglected to include any tests...
>
> I will look into adding tests.  I've also been told that there may be
> some limits for the .pgpass file, so I am looking into that as well.

Ok.

Thanks!

Stephen

Attachment

Re: Maximum password length

From
Tom Lane
Date:
Isaac Morland <isaac.morland@gmail.com> writes:
> On Fri, 12 Oct 2018 at 16:52, Stephen Frost <sfrost@snowman.net> wrote:
>> I'm also trying to figure out why it makes sense to support an 8k
>> password and if we've really tried seeing what happens if pg_authid gets
>> a toast table that's actually used for passwords...

> ...
> It's also obvious that past a certain point, longer passwords don't help
> anyway, because it's already enough to have a password that can't be
> guessed in, say, the expected duration of the Earth's existence using all
> the computing power currently available in the world.

And, of course, who is really going to type a password longer than a
couple dozen characters?  And get it right reliably, when they can't
see what they're typing?  But even if you assume the password is never
manually entered but just lives in somebody's .pgpass, it's pointless
to make it so long.  Then the attacker will just switch to brute-forcing
the user's login password, or whereever along the chain there actually
is a manually-entered password.

I concur that we might as well standardize on something in the range
of 64 to 100 characters.  1K is silly, even if somewhere there is a
spec that allows it.

            regards, tom lane


Re: Maximum password length

From
Stephen Frost
Date:
Greetings,

* Bossart, Nathan (bossartn@amazon.com) wrote:
> On 10/12/18, 4:04 PM, "Isaac Morland" <isaac.morland@gmail.com> wrote:
> > I agree there should be a specific limit that is the same in libpq,
> > on the server, and in the protocol. Maybe 128 characters, to get a
> > nice round number? This is still way longer than the 32-byte SHA 256
> > hash. Or 64, which is still plenty but doesn't involve extending the
> > current character buffer size to a longer value while still hugely
> > exceeding the amount of information in the hash.
>
> My main motivation for suggesting the increase to 8k is to provide
> flexibility for alternative authentication methods like LDAP, RADIUS,
> PAM, and BSD.

Specific use-cases here would be better than hand-waving at "these other
things."  Last I checked, all of those work with what we've got today
and I don't recall hearing complaints about them not working due to this
limit.

Thanks!

Stephen

Attachment

Re: Maximum password length

From
"Bossart, Nathan"
Date:
On 10/12/18, 4:24 PM, "Stephen Frost" <sfrost@snowman.net> wrote:
> * Bossart, Nathan (bossartn@amazon.com) wrote:
>> My main motivation for suggesting the increase to 8k is to provide
>> flexibility for alternative authentication methods like LDAP, RADIUS,
>> PAM, and BSD.
>
> Specific use-cases here would be better than hand-waving at "these other
> things."  Last I checked, all of those work with what we've got today
> and I don't recall hearing complaints about them not working due to this
> limit.

The main one I am thinking of is generated security tokens.  It seems
reasonable to me to limit md5 and scram-sha-256 passwords to a much
shorter length, but I think the actual server message limit should be
somewhat more flexible.

Nathan


Re: Maximum password length

From
Tom Lane
Date:
"Bossart, Nathan" <bossartn@amazon.com> writes:
> On 10/12/18, 4:24 PM, "Stephen Frost" <sfrost@snowman.net> wrote:
>> Specific use-cases here would be better than hand-waving at "these other
>> things."  Last I checked, all of those work with what we've got today
>> and I don't recall hearing complaints about them not working due to this
>> limit.

> The main one I am thinking of is generated security tokens.  It seems
> reasonable to me to limit md5 and scram-sha-256 passwords to a much
> shorter length, but I think the actual server message limit should be
> somewhat more flexible.

Sure, but even a generated security token seems unlikely to be more
than a couple dozen bytes long.  What's the actual use-case for tokens
longer than that?  ISTM that a limit around 100 bytes already has a
whole lot of headroom.

            regards, tom lane


Re: Maximum password length

From
"Bossart, Nathan"
Date:
On 10/12/18, 7:02 PM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
> "Bossart, Nathan" <bossartn@amazon.com> writes:
>> The main one I am thinking of is generated security tokens.  It seems
>> reasonable to me to limit md5 and scram-sha-256 passwords to a much
>> shorter length, but I think the actual server message limit should be
>> somewhat more flexible.
>
> Sure, but even a generated security token seems unlikely to be more
> than a couple dozen bytes long.  What's the actual use-case for tokens
> longer than that?  ISTM that a limit around 100 bytes already has a
> whole lot of headroom.

I can't speak to the technical necessity of longer tokens, but several
services provide them.  One specific example is the AWS Security Token
Service.  The documentation for that service currently suggests that
"the typical size is less than 4096 bytes..." [0].  I understand this
alone doesn't warrant a change to PostgreSQL, but it seems valuable to
me to ease this restriction on custom client authentication
mechanisms.

Regarding md5 and scram-sha-256 passwords, I'll look into establishing
some sort of maximum password length that is well-documented and
provides users with clear error messages.  My vote would be something
like 128 characters just to be safe.  One interesting question is how
we handle existing longer passwords after upgrading.  Maybe we could
continue to allow longer passwords to be used for authentication and
only restrict the length of new ones.

Nathan

[0] https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html


Re: Maximum password length

From
Alexander Kukushkin
Date:
On Sat, 13 Oct 2018 at 02:02, Tom Lane <tgl@sss.pgh.pa.us> wrote:

> Sure, but even a generated security token seems unlikely to be more
> than a couple dozen bytes long.  What's the actual use-case for tokens
> longer than that?  ISTM that a limit around 100 bytes already has a
> whole lot of headroom.

Self-containing tokens, for example JWT, could be easily longer than 100 bytes.
We at Zalando are using such tokens and the usual size of JWT token is
600-700 bytes.

It is not possible to "paste" such token into psql password prompt,
because the input is truncated by 100 bytes.
It is not possible to put it into ".pgpass" either, because it assumes
that line could not be longer than 320 bytes (64*5)

At the moment there are only two ways to use such tokens as a password:
1. export PGPASSWORD=very_long.token
2. specify the token(password) in the connection url

Regards,
--
Alexander Kukushkin


Re: Maximum password length

From
Tom Lane
Date:
Alexander Kukushkin <cyberdemn@gmail.com> writes:
> Self-containing tokens, for example JWT, could be easily longer than 100 bytes.
> We at Zalando are using such tokens and the usual size of JWT token is
> 600-700 bytes.

> It is not possible to "paste" such token into psql password prompt,
> because the input is truncated by 100 bytes.
> It is not possible to put it into ".pgpass" either, because it assumes
> that line could not be longer than 320 bytes (64*5)

> At the moment there are only two ways to use such tokens as a password:
> 1. export PGPASSWORD=very_long.token
> 2. specify the token(password) in the connection url

This thread seems to have fallen off the radar, but I got interested again
now that we have a report of somebody else trying to use an 800-or-so-byte
password [1], so I looked over Nathan's patches in some detail.

I concur with Stephen's position that there ought to be just one upper
limit not several.  At the same time, it's not clear to me that the
password packet's length is closely related to the plaintext password
limit when we're using SCRAM --- is there any case where the verifier
string could exceed a few hundred bytes?

Also, I'm not exactly convinced that we need to document the limit in the
SGML docs, and I'm definitely down on repeating that info in 16 different
places.  If we make the limit high enough to not be a problem, nobody is
going to care exactly what it is.

Therefore, I propose setting this up with a #define symbol in
pg_config_manual.h and leaving it at that.  Giving documentation in
pg_config_manual.h seems sufficient to me.  Attached is a revised
version of Nathan's patches that does it like that.

I set the proposed limit at 1024 bytes, but given that we now know
of use-cases needing up to 800 bytes, maybe there should be a little
more headroom?  I don't want to make it enormous, though, seeing that
we're allocating static buffers of that size.

Note this patch is intended to be applied over my patch at [2],
since it modifies the test case added there.

            regards, tom lane

[1] https://www.postgresql.org/message-id/flat/CAOhmDze1nqG2vfegpSsTFCgaiFRsqgjO6yLsbmhroz2zGmJHog%40mail.gmail.com
[2] https://www.postgresql.org/message-id/4187382.1598909041%40sss.pgh.pa.us

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 91b7958c48..c83355c6e6 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -294,7 +294,7 @@ sql_conn(struct options *my_opts)
 {
     PGconn       *conn;
     bool        have_password = false;
-    char        password[100];
+    char        password[MAX_PG_PASSWORD_LEN + 1];
     bool        new_pass;
     PGresult   *res;

diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index e4019fafaa..74d72ca40a 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -70,7 +70,7 @@ vacuumlo(const char *database, const struct _param *param)
     bool        new_pass;
     bool        success = true;
     static bool have_password = false;
-    static char password[100];
+    static char password[MAX_PG_PASSWORD_LEN + 1];

     /* Note: password can be carried over from a previous call */
     if (param->pg_prompt == TRI_YES && !have_password)
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 02b6c3f127..af3ceb945a 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -697,8 +697,12 @@ recv_password_packet(Port *port)
             return NULL;        /* EOF */
     }

+    /*
+     * Receive the password.  Allow the password proper (not counting the null
+     * terminator or message length word) to be up to MAX_PG_PASSWORD_LEN.
+     */
     initStringInfo(&buf);
-    if (pq_getmessage(&buf, 1000))    /* receive password */
+    if (pq_getmessage(&buf, MAX_PG_PASSWORD_LEN + 1 + 4))
     {
         /* EOF - pq_getmessage already logged a suitable message */
         pfree(buf.data);
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 786672b1b6..a7ac748dd0 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1481,8 +1481,8 @@ setup_auth(FILE *cmdfd)
 static void
 get_su_pwd(void)
 {
-    char        pwd1[100];
-    char        pwd2[100];
+    char        pwd1[MAX_PG_PASSWORD_LEN + 1];
+    char        pwd2[MAX_PG_PASSWORD_LEN + 1];

     if (pwprompt)
     {
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index c08003e7f2..27423f460c 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -50,7 +50,7 @@ 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[MAX_PG_PASSWORD_LEN + 1];
 PGconn       *conn = NULL;

 /*
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 94af11b80a..9b2d8bbe50 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -122,7 +122,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
     const char *newdb;
     const char *newuser;
     char       *password;
-    char        passbuf[100];
+    char        passbuf[MAX_PG_PASSWORD_LEN + 1];
     bool        new_pass;

     if (!reqdb)
@@ -242,7 +242,7 @@ ConnectDatabase(Archive *AHX,
 {
     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     char       *password;
-    char        passbuf[100];
+    char        passbuf[MAX_PG_PASSWORD_LEN + 1];
     bool        new_pass;

     if (AH->connection)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 2c82b39af0..463996d8df 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -1644,7 +1644,7 @@ connectDatabase(const char *dbname, const char *connection_string,
     const char **values = NULL;
     PQconninfoOption *conn_opts = NULL;
     static bool have_password = false;
-    static char password[100];
+    static char password[MAX_PG_PASSWORD_LEN + 1];

     if (prompt_password == TRI_YES && !have_password)
     {
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 08a5947a9e..dbe11331ab 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1175,7 +1175,7 @@ doConnect(void)
     PGconn       *conn;
     bool        new_pass;
     static bool have_password = false;
-    static char password[100];
+    static char password[MAX_PG_PASSWORD_LEN + 1];

     /*
      * Start the connection.  Loop until we have a password if requested by
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a2ba..2c6880d02e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1964,8 +1964,8 @@ 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[MAX_PG_PASSWORD_LEN + 1];
+        char        pw2[MAX_PG_PASSWORD_LEN + 1];

         simple_prompt("Enter new password: ", pw1, sizeof(pw1), false);
         simple_prompt("Enter it again: ", pw2, sizeof(pw2), false);
@@ -2982,7 +2982,7 @@ copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
 static char *
 prompt_for_password(const char *username)
 {
-    char        buf[100];
+    char        buf[MAX_PG_PASSWORD_LEN + 1];

     if (username == NULL || username[0] == '\0')
         simple_prompt("Password: ", buf, sizeof(buf), false);
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 3302bd4dd3..127919ee10 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -120,7 +120,7 @@ main(int argc, char *argv[])
     struct adhoc_opts options;
     int            successResult;
     bool        have_password = false;
-    char        password[100];
+    char        password[MAX_PG_PASSWORD_LEN + 1];
     bool        new_pass;

     pg_logging_init(argv[0]);
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index 420d0d11a5..447a61d201 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -69,7 +69,7 @@ connectDatabase(const char *dbname, const char *pghost,
     PGconn       *conn;
     bool        new_pass;
     static bool have_password = false;
-    static char password[100];
+    static char password[MAX_PG_PASSWORD_LEN + 1];

     if (!allow_password_reuse)
         have_password = false;
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 9ced079ac7..70c346271e 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -64,7 +64,7 @@ main(int argc, char *argv[])
     bool        pwprompt = false;
     char       *newpassword = NULL;
     char        newuser_buf[128];
-    char        newpassword_buf[100];
+    char        newpassword_buf[MAX_PG_PASSWORD_LEN + 1];

     /* Tri-valued variables.  */
     enum trivalue createdb = TRI_DEFAULT,
@@ -206,7 +206,7 @@ main(int argc, char *argv[])

     if (pwprompt)
     {
-        char        pw2[100];
+        char        pw2[MAX_PG_PASSWORD_LEN + 1];

         simple_prompt("Enter password for new role: ",
                       newpassword_buf, sizeof(newpassword_buf), false);
diff --git a/src/common/saslprep.c b/src/common/saslprep.c
index 2dedf6b0fb..6b4967efc2 100644
--- a/src/common/saslprep.c
+++ b/src/common/saslprep.c
@@ -29,12 +29,6 @@
 #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.
@@ -1079,7 +1073,7 @@ pg_saslprep(const char *input, char **output)
     *output = NULL;

     /* Check that the password isn't stupendously long */
-    if (strlen(input) > MAX_PASSWORD_LENGTH)
+    if (strlen(input) > MAX_PG_PASSWORD_LEN)
     {
 #ifndef FRONTEND
         ereport(ERROR,
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 705dc69c06..d2b9255a60 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -97,6 +97,18 @@
  */
 #define MAXPGPATH        1024

+/*
+ * MAX_PG_PASSWORD_LEN: maximum length of a password string.
+ *
+ * This is applied indiscriminately to both plaintext and hashed passwords,
+ * so it must be large enough to accommodate all hashed-password formats.
+ * There are known use-cases in which the password is a machine-generated
+ * security token ranging up to around 800 bytes.
+ * Note that some auth methods have a tighter limit, for example RADIUS
+ * auth is limited to 128-byte passwords according to the RADIUS standard.
+ */
+#define MAX_PG_PASSWORD_LEN        1024
+
 /*
  * PG_SOMAXCONN: maximum accept-queue length limit passed to
  * listen(2).  You'd think we should use SOMAXCONN from
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 1b4323fe2a..6a95814784 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -60,13 +60,14 @@ $node->start;

 # Create 3 roles with different password methods for each one. The same
 # password is used for all of them.
+my $password = 'a' . 'b' x 1000 . 'c';
 $node->safe_psql('postgres',
-    "SET password_encryption='scram-sha-256'; CREATE ROLE scram_role LOGIN PASSWORD 'pass';"
+    "SET password_encryption='scram-sha-256'; CREATE ROLE scram_role LOGIN PASSWORD '$password';"
 );
 $node->safe_psql('postgres',
-    "SET password_encryption='md5'; CREATE ROLE md5_role LOGIN PASSWORD 'pass';"
+    "SET password_encryption='md5'; CREATE ROLE md5_role LOGIN PASSWORD '$password';"
 );
-$ENV{"PGPASSWORD"} = 'pass';
+$ENV{"PGPASSWORD"} = $password;

 # For "trust" method, all users should be able to connect.
 reset_pg_hba($node, 'trust');
@@ -108,7 +109,7 @@ $ENV{"PGPASSFILE"} = $pgpassfile;

 append_to_file($pgpassfile, qq!
 # This very long comment is just here to exercise handling of long lines in the file. This very long comment is just
hereto exercise handling of long lines in the file. This very long comment is just here to exercise handling of long
linesin the file. This very long comment is just here to exercise handling of long lines in the file. This very long
commentis just here to exercise handling of long lines in the file. 
-*:*:postgres:scram_role:pass:this is not part of the password.
+*:*:postgres:scram_role:$password:this is not part of the password.
 !);
 chmod 0600, $pgpassfile or die;

@@ -116,8 +117,11 @@ reset_pg_hba($node, 'password');
 test_role($node, 'scram_role', 'password from pgpass', 0);
 test_role($node, 'md5_role',   'password from pgpass', 2);

+my $modpassword = $password;
+$modpassword =~ s/b/\\b/g;
+
 append_to_file($pgpassfile, qq!
-*:*:*:md5_role:p\\ass
+*:*:*:md5_role:$modpassword
 !);

 test_role($node, 'md5_role',   'password from pgpass', 0);

Re: Maximum password length

From
Tom Lane
Date:
I wrote:
> Note this patch is intended to be applied over my patch at [2],
> since it modifies the test case added there.

I've now pushed that patch, so the patch in my previous mail should
directly apply to HEAD.

I'd originally been wondering whether we need to back-patch this patch.
But unless someone wants to make a case for the max password length
being more than 1024, it seems like this is mostly cleanup and could
just be done in HEAD.  At 1024, the actual behavior of pg_saslprep()
isn't changing at all, and the behavior of recv_password_packet() isn't
changing by much.  The real impact is just that the places that prompt
for a password will accept passwords up to 1K instead of 100 bytes.
Which, TBH, seems like neatnik-ism rather than fixing anything useful.
Surely nobody is going to manually enter passwords exceeding 100 bytes.
And, since simple_prompt insists on reading /dev/tty not stdin, there
is no very easy way to pass a machine-generated password through that
code path.  The practical ways to deal with a long password are either
to set it as PGPASSWORD (has always worked) or put it in .pgpass
(works as of now).

Anyway, I added this thread to the upcoming CF, in case anyone wants to
discuss it further.

            regards, tom lane



Re: Maximum password length

From
"Bossart, Nathan"
Date:
On 8/31/20, 5:55 PM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
> I set the proposed limit at 1024 bytes, but given that we now know
> of use-cases needing up to 800 bytes, maybe there should be a little
> more headroom?  I don't want to make it enormous, though, seeing that
> we're allocating static buffers of that size.

For the use-case described in [0], I ended up bumping the server-side
limit in libpq/auth.c to 8192 bytes for RDS instances.  This appears
to be the PqRecvBuffer size, too.  In any case, these tokens regularly
exceed 1024 bytes, so I would definitely argue for more headroom if
possible.  Otherwise, I like the idea of unifying all the limits.

Nathan

[0] https://www.postgresql.org/message-id/flat/CAOhmDze1nqG2vfegpSsTFCgaiFRsqgjO6yLsbmhroz2zGmJHog%40mail.gmail.com


Re: Maximum password length

From
Peter Eisentraut
Date:
On 2020-09-01 02:54, Tom Lane wrote:
> Therefore, I propose setting this up with a #define symbol in
> pg_config_manual.h and leaving it at that.  Giving documentation in
> pg_config_manual.h seems sufficient to me.  Attached is a revised
> version of Nathan's patches that does it like that.
> 
> I set the proposed limit at 1024 bytes, but given that we now know
> of use-cases needing up to 800 bytes, maybe there should be a little
> more headroom?  I don't want to make it enormous, though, seeing that
> we're allocating static buffers of that size.

ISTM that it's only going to be a matter of time before that will be 
exceeded.  Why have a limit at all?  Accept whatever StringInfo accepts.

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: Maximum password length

From
Tom Lane
Date:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
> ISTM that it's only going to be a matter of time before that will be 
> exceeded.  Why have a limit at all?  Accept whatever StringInfo accepts.

Hmm, that would require some refactoring of simple_prompt for starters.
I agree there's no hard reason why we have to have any specific limit,
if we're willing to do that.

            regards, tom lane



Re: Maximum password length

From
Tom Lane
Date:
I wrote:
> Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
>> ISTM that it's only going to be a matter of time before that will be 
>> exceeded.  Why have a limit at all?  Accept whatever StringInfo accepts.

> Hmm, that would require some refactoring of simple_prompt for starters.

To use StringInfo, we have to move sprompt.c into src/common/ where
the stringinfo stuff lives; but that seems fine to me, because it had
little if any business being in src/port/.  Here's a draft patch
that does it that way.

This could be refined; in particular, I think that most of the
password-prompting sites could drop their separate have_password
flags in favor of checking whether the password pointer is NULL
or not.  That would likely also prove that some of the free(password)
calls I sprinkled in are unnecessary.

            regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 91b7958c48..753996585d 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -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"
@@ -294,7 +295,7 @@ sql_conn(struct options *my_opts)
 {
     PGconn       *conn;
     bool        have_password = false;
-    char        password[100];
+    char       *password = NULL;
     bool        new_pass;
     PGresult   *res;

@@ -339,7 +340,9 @@ sql_conn(struct options *my_opts)
             !have_password)
         {
             PQfinish(conn);
-            simple_prompt("Password: ", password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt("Password: ", false);
             have_password = true;
             new_pass = true;
         }
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index e4019fafaa..e09362cc51 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -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"
@@ -70,12 +71,14 @@ vacuumlo(const char *database, const struct _param *param)
     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);
+        if (password)
+            free(password);
+        password = simple_prompt("Password: ", false);
         have_password = true;
     }

@@ -119,7 +122,9 @@ vacuumlo(const char *database, const struct _param *param)
             param->pg_prompt != TRI_NO)
         {
             PQfinish(conn);
-            simple_prompt("Password: ", password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt("Password: ", false);
             have_password = true;
             new_pass = true;
         }
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 02b6c3f127..36565df4fc 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -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);
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 786672b1b6..b62f8f6a5e 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -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,7 @@ get_su_pwd(void)
          * for now.
          */
         FILE       *pwf = fopen(pwfilename, "r");
-        int            i;
+        char        pwdbuf[8192];

         if (!pwf)
         {
@@ -1518,7 +1521,7 @@ get_su_pwd(void)
                          pwfilename);
             exit(1);
         }
-        if (!fgets(pwd1, sizeof(pwd1), pwf))
+        if (!fgets(pwdbuf, sizeof(pwdbuf), pwf))
         {
             if (ferror(pwf))
                 pg_log_error("could not read password from file \"%s\": %m",
@@ -1530,12 +1533,11 @@ 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(pwdbuf);
+        pwd1 = pg_strdup(pwdbuf);
     }

-    superuser_password = pg_strdup(pwd1);
+    superuser_password = pwd1;
 }

 /*
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index c08003e7f2..a74f866d41 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -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"
@@ -50,7 +51,7 @@ 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;

 /*
@@ -157,7 +158,9 @@ GetConnection(void)
         /* Get a new password if appropriate */
         if (need_password)
         {
-            simple_prompt("Password: ", password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt("Password: ", false);
             have_password = true;
             need_password = false;
         }
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 94af11b80a..12899e26e2 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -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.
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 2c82b39af0..bc8d2d61ab 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -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"
@@ -1644,11 +1645,13 @@ connectDatabase(const char *dbname, const char *connection_string,
     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);
+        if (password)
+            free(password);
+        password = simple_prompt("Password: ", false);
         have_password = true;
     }

@@ -1761,7 +1764,9 @@ connectDatabase(const char *dbname, const char *connection_string,
             prompt_password != TRI_NO)
         {
             PQfinish(conn);
-            simple_prompt("Password: ", password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt("Password: ", false);
             have_password = true;
             new_pass = true;
         }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 08a5947a9e..ff2e6226a9 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -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"
@@ -1175,7 +1176,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
@@ -1218,7 +1219,9 @@ doConnect(void)
             !have_password)
         {
             PQfinish(conn);
-            simple_prompt("Password: ", password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt("Password: ", false);
             have_password = true;
             new_pass = true;
         }
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a2ba..d4aa0976b5 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -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
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 3302bd4dd3..573670fb28 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -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"
@@ -120,7 +121,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,7 +234,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);
+        password = simple_prompt("Password: ", false);
         have_password = true;
     }

@@ -287,7 +288,9 @@ main(int argc, char *argv[])
                 password_prompt = pg_strdup(_("Password: "));
             PQfinish(pset.db);

-            simple_prompt(password_prompt, password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt(password_prompt, false);
             free(password_prompt);
             have_password = true;
             new_pass = true;
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index 420d0d11a5..2193df0b4c 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -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"

@@ -69,14 +70,16 @@ 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)
     {
-        simple_prompt("Password: ", password, sizeof(password), false);
+        if (password)
+            free(password);
+        password = simple_prompt("Password: ", false);
         have_password = true;
     }

@@ -122,7 +125,9 @@ connectDatabase(const char *dbname, const char *pghost,
             prompt_password != TRI_NO)
         {
             PQfinish(conn);
-            simple_prompt("Password: ", password, sizeof(password), false);
+            if (password)
+                free(password);
+            password = simple_prompt("Password: ", false);
             have_password = true;
             new_pass = true;
         }
@@ -444,14 +449,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));
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 9ced079ac7..6179199563 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -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)
diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index fee270d4f6..f7ddd1402d 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -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
         {
diff --git a/src/common/Makefile b/src/common/Makefile
index 16619e4ba8..ae05247631 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -92,7 +92,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/saslprep.c b/src/common/saslprep.c
index 2dedf6b0fb..d60452f75f 100644
--- a/src/common/saslprep.c
+++ b/src/common/saslprep.c
@@ -29,12 +29,6 @@
 #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.
diff --git a/src/port/sprompt.c b/src/common/sprompt.c
similarity index 82%
rename from src/port/sprompt.c
rename to src/common/sprompt.c
index 6d8a8b2609..71be4903b5 100644
--- a/src/port/sprompt.c
+++ b/src/common/sprompt.c
@@ -8,12 +8,15 @@
  *
  *
  * IDENTIFICATION
- *      src/port/sprompt.c
+ *      src/common/sprompt.c
  *
  *-------------------------------------------------------------------------
  */
 #include "c.h"

+#include "common/string.h"
+#include "lib/stringinfo.h"
+
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
@@ -26,20 +29,17 @@
  * 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;
     FILE       *termin,
                *termout;
-
+    StringInfoData buf;
 #if defined(HAVE_TERMIOS_H)
     struct termios t_orig,
                 t;
@@ -126,29 +126,25 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
         fflush(termout);
     }

-    if (fgets(destination, destlen, termin) == NULL)
-        destination[0] = '\0';
+    initStringInfo(&buf);

-    length = strlen(destination);
-    if (length > 0 && destination[length - 1] != '\n')
+    while (!feof(termin) && !ferror(termin))
     {
-        /* 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');
+        /* Make sure there's a reasonable amount of room in the buffer */
+        enlargeStringInfo(&buf, 128);
+
+        /* Read some data, appending it to what we already have */
+        if (fgets(buf.data + buf.len, buf.maxlen - buf.len, termin) == NULL)
+            break;
+        buf.len += strlen(buf.data + buf.len);
+
+        /* Done if we have a whole line, else loop to read more */
+        if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
+            break;
     }

     /* 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(buf.data);

     if (!echo)
     {
@@ -169,4 +165,6 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
         fclose(termin);
         fclose(termout);
     }
+
+    return buf.data;
 }
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 5113c04434..08026c8898 100644
--- a/src/include/common/string.h
+++ b/src/include/common/string.h
@@ -10,10 +10,14 @@
 #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/sprompt.c */
+extern char *simple_prompt(const char *prompt, bool echo);
+
 #endif                            /* COMMON_STRING_H */
diff --git a/src/include/port.h b/src/include/port.h
index 271ff0d00b..84bf2c363f 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -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. */
diff --git a/src/port/Makefile b/src/port/Makefile
index 8defa1257b..e41b005c4f 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -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
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 20da7985c1..3682095a6e 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -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');
@@ -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.o));

     our @pgcommonbkndfiles = @pgcommonallfiles;


Re: Maximum password length

From
Tom Lane
Date:
I wrote:
> This could be refined; in particular, I think that most of the
> password-prompting sites could drop their separate have_password
> flags in favor of checking whether the password pointer is NULL
> or not.  That would likely also prove that some of the free(password)
> calls I sprinkled in are unnecessary.

Hearing no objections to this general plan, I went ahead and did that
cleanup.  This version seems committable to me.

            regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 91b7958c48..5a884e2904 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -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);
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index e4019fafaa..532cc596c4 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -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);
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 02b6c3f127..36565df4fc 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -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);
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 786672b1b6..b62f8f6a5e 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -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,7 @@ get_su_pwd(void)
          * for now.
          */
         FILE       *pwf = fopen(pwfilename, "r");
-        int            i;
+        char        pwdbuf[8192];

         if (!pwf)
         {
@@ -1518,7 +1521,7 @@ get_su_pwd(void)
                          pwfilename);
             exit(1);
         }
-        if (!fgets(pwd1, sizeof(pwd1), pwf))
+        if (!fgets(pwdbuf, sizeof(pwdbuf), pwf))
         {
             if (ferror(pwf))
                 pg_log_error("could not read password from file \"%s\": %m",
@@ -1530,12 +1533,11 @@ 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(pwdbuf);
+        pwd1 = pg_strdup(pwdbuf);
     }

-    superuser_password = pg_strdup(pwd1);
+    superuser_password = pwd1;
 }

 /*
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index c08003e7f2..be653ebb2d 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -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;
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 94af11b80a..12899e26e2 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -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.
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 2c82b39af0..97d2b8dac1 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -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);
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 08a5947a9e..332eabf637 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -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);
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a2ba..d4aa0976b5 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -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
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 3302bd4dd3..8232a0143b 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -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);
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index 420d0d11a5..e987eef234 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -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));
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 9ced079ac7..6179199563 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -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)
diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index fee270d4f6..f7ddd1402d 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -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
         {
diff --git a/src/common/Makefile b/src/common/Makefile
index 16619e4ba8..ae05247631 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -92,7 +92,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/saslprep.c b/src/common/saslprep.c
index 2dedf6b0fb..d60452f75f 100644
--- a/src/common/saslprep.c
+++ b/src/common/saslprep.c
@@ -29,12 +29,6 @@
 #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.
diff --git a/src/port/sprompt.c b/src/common/sprompt.c
similarity index 82%
rename from src/port/sprompt.c
rename to src/common/sprompt.c
index 6d8a8b2609..71be4903b5 100644
--- a/src/port/sprompt.c
+++ b/src/common/sprompt.c
@@ -8,12 +8,15 @@
  *
  *
  * IDENTIFICATION
- *      src/port/sprompt.c
+ *      src/common/sprompt.c
  *
  *-------------------------------------------------------------------------
  */
 #include "c.h"

+#include "common/string.h"
+#include "lib/stringinfo.h"
+
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
@@ -26,20 +29,17 @@
  * 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;
     FILE       *termin,
                *termout;
-
+    StringInfoData buf;
 #if defined(HAVE_TERMIOS_H)
     struct termios t_orig,
                 t;
@@ -126,29 +126,25 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
         fflush(termout);
     }

-    if (fgets(destination, destlen, termin) == NULL)
-        destination[0] = '\0';
+    initStringInfo(&buf);

-    length = strlen(destination);
-    if (length > 0 && destination[length - 1] != '\n')
+    while (!feof(termin) && !ferror(termin))
     {
-        /* 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');
+        /* Make sure there's a reasonable amount of room in the buffer */
+        enlargeStringInfo(&buf, 128);
+
+        /* Read some data, appending it to what we already have */
+        if (fgets(buf.data + buf.len, buf.maxlen - buf.len, termin) == NULL)
+            break;
+        buf.len += strlen(buf.data + buf.len);
+
+        /* Done if we have a whole line, else loop to read more */
+        if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
+            break;
     }

     /* 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(buf.data);

     if (!echo)
     {
@@ -169,4 +165,6 @@ simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
         fclose(termin);
         fclose(termout);
     }
+
+    return buf.data;
 }
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 5113c04434..08026c8898 100644
--- a/src/include/common/string.h
+++ b/src/include/common/string.h
@@ -10,10 +10,14 @@
 #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/sprompt.c */
+extern char *simple_prompt(const char *prompt, bool echo);
+
 #endif                            /* COMMON_STRING_H */
diff --git a/src/include/port.h b/src/include/port.h
index 271ff0d00b..84bf2c363f 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -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. */
diff --git a/src/port/Makefile b/src/port/Makefile
index 8defa1257b..e41b005c4f 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -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
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 20da7985c1..1a731e834f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -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');
@@ -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;


Re: Maximum password length

From
"Bossart, Nathan"
Date:
On 9/3/20, 10:19 AM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
> Hearing no objections to this general plan, I went ahead and did that
> cleanup.  This version seems committable to me.

                FILE       *pwf = fopen(pwfilename, "r");
-               int                     i;
+               char            pwdbuf[8192];

If I am reading correctly, this would be the only defined password
length limit once this patch is applied.  While it's probably unlikely
that this will cause problems for anybody anytime soon, is there any
reason not to give this the same treatment as the .pgpass code and
remove the line length limit altogether?

Otherwise, the patch looks good to me.

Nathan


Re: Maximum password length

From
Tom Lane
Date:
"Bossart, Nathan" <bossartn@amazon.com> writes:
> On 9/3/20, 10:19 AM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
> +               char            pwdbuf[8192];

> If I am reading correctly, this would be the only defined password
> length limit once this patch is applied.  While it's probably unlikely
> that this will cause problems for anybody anytime soon, is there any
> reason not to give this the same treatment as the .pgpass code and
> remove the line length limit altogether?

Yeah, it just didn't quite seem worthwhile there, given the adjacent
comment that clearly says that this is second-class-citizen code:

         * Ideally this should insist that the file not be world-readable.
         * However, this option is mainly intended for use on Windows where
         * file permissions may not exist at all, so we'll skip the paranoia
         * for now.

If you insist, I'll change it, but it seems even less likely to ever
matter to anybody than the changes to make simple_prompt accept
indefinitely long passwords.  (Perhaps a reasonable compromise
is to extend this comment to note that we're also not bothering
to support indefinitely long passwords.)

            regards, tom lane



Re: Maximum password length

From
"Bossart, Nathan"
Date:
On 9/3/20, 2:14 PM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
> If you insist, I'll change it, but it seems even less likely to ever
> matter to anybody than the changes to make simple_prompt accept
> indefinitely long passwords.  (Perhaps a reasonable compromise
> is to extend this comment to note that we're also not bothering
> to support indefinitely long passwords.)

I don't feel strongly about this.  A comment in the code seems
reasonable.

Nathan


Re: Maximum password length

From
Tom Lane
Date:
"Bossart, Nathan" <bossartn@amazon.com> writes:
> On 9/3/20, 2:14 PM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
>> If you insist, I'll change it, but it seems even less likely to ever
>> matter to anybody than the changes to make simple_prompt accept
>> indefinitely long passwords.  (Perhaps a reasonable compromise
>> is to extend this comment to note that we're also not bothering
>> to support indefinitely long passwords.)

> I don't feel strongly about this.  A comment in the code seems
> reasonable.

Alvaro proposes nearby that we ought to have a src/common/ function
to slurp an indefinitely long line from a file [1].  If we do that,
it'd be entirely reasonable to make this code use that.  So maybe
the right comment is "XXX FIXME later".

            regards, tom lane

[1] https://www.postgresql.org/message-id/20200903200842.GA11952%40alvherre.pgsql



Re: Maximum password length

From
Tom Lane
Date:
I wrote:
> Alvaro proposes nearby that we ought to have a src/common/ function
> to slurp an indefinitely long line from a file [1].  If we do that,
> it'd be entirely reasonable to make this code use that.  So maybe
> the right comment is "XXX FIXME later".

Actually, on further thought, the obviously right thing to do here
is to refactor simple_prompt into two functions: the inner one is
basically like fgets except it returns a malloc'd, variable-size
string, and then the outer one does the other stuff simple_prompt needs
such as prompting and opening /dev/tty.  The inner function would
serve initdb's need directly, and it would also have uses elsewhere,
as per the other thread.

I'll go make that happen.

            regards, tom lane