Maximum password length

Started by Bossart, Nathanover 7 years ago25 messages
#1Bossart, Nathan
bossartn@amazon.com
3 attachment(s)

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]https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/common/saslprep.c;h=4cf574fed87ad830bcf8fdb105e37f8b4df0ee44;hb=HEAD#l42, 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]https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/libpq/auth.c;h=85175655359829a2cf50dd883066bbb3d45e2286;hb=HEAD#l682, 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

Attachments:

v1-0002-Add-documentation-regarding-effective-password-le.patchapplication/octet-stream; name=v1-0002-Add-documentation-regarding-effective-password-le.patchDownload
From 418697e051b36dcdcb1a07b79717e7c81629e36a Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Fri, 12 Oct 2018 17:26:37 +0000
Subject: [PATCH v1 2/3] Add documentation regarding effective password length
 limits.

---
 doc/src/sgml/client-auth.sgml        | 80 ++++++++++++++++++++++++++++++++++++
 doc/src/sgml/libpq.sgml              | 10 +++++
 doc/src/sgml/ref/clusterdb.sgml      | 16 ++++++++
 doc/src/sgml/ref/createdb.sgml       | 16 ++++++++
 doc/src/sgml/ref/createuser.sgml     | 21 ++++++++++
 doc/src/sgml/ref/dropdb.sgml         | 16 ++++++++
 doc/src/sgml/ref/dropuser.sgml       | 16 ++++++++
 doc/src/sgml/ref/pg_basebackup.sgml  | 16 ++++++++
 doc/src/sgml/ref/pg_dump.sgml        | 16 ++++++++
 doc/src/sgml/ref/pg_dumpall.sgml     | 16 ++++++++
 doc/src/sgml/ref/pg_receivewal.sgml  | 16 ++++++++
 doc/src/sgml/ref/pg_recvlogical.sgml | 16 ++++++++
 doc/src/sgml/ref/pg_restore.sgml     | 16 ++++++++
 doc/src/sgml/ref/psql-ref.sgml       | 16 ++++++++
 doc/src/sgml/ref/reindexdb.sgml      | 16 ++++++++
 doc/src/sgml/ref/vacuumdb.sgml       | 16 ++++++++
 16 files changed, 319 insertions(+)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index c2114021c3..1ce581ae0a 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -994,6 +994,26 @@ omicron         bryanh                  guest1
     </varlistentry>
    </variablelist>
 
+   <note>
+    <para>
+     While <literal>md5</literal> passwords do not have a defined maximum length
+     and <literal>scram-sha-256</literal> passwords may be up to 1024 characters
+     long, many client utilities (e.g. <application>psql</application>) will
+     truncate passwords provided via prompt to 99 characters.  Longer passwords
+     may be specified using alternatives to password prompts such as the
+     <filename>.pgpass</filename> file (<xref linkend="libpq-pgpass"/>) and the
+     <envar>PGPASSWORD</envar> environment variable
+     (<xref linkend="libpq-envars"/>).
+    </para>
+    <para>
+     Furthermore, it should be noted that the server restricts password messages
+     to an effective limit of 995 characters.  While this is enough for the
+     <literal>scram-sha-256</literal> and <literal>md5</literal> authentication
+     methods, it may not be enough for passwords sent in clear-text via the
+     method <literal>password</literal>.
+    </para>
+   </note>
+
    <para>
     <productname>PostgreSQL</productname> database passwords are
     separate from operating system user passwords. The password for
@@ -1482,6 +1502,21 @@ omicron         bryanh                  guest1
     two separate connections to the LDAP server to be made.
    </para>
 
+   <note>
+    <para>
+     While PostgreSQL's LDAP authentication method does not have a defined
+     maximum password length, many client utilities (e.g.
+     <application>psql</application>) will truncate passwords provided via
+     prompt to 99 characters.  Longer passwords may be specified using
+     alternatives to password prompts such as the <filename>.pgpass</filename>
+     file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+     environment variable (<xref linkend="libpq-envars"/>).  However, it should
+     be noted that the server restricts password messages to an effective limit
+     of 995 characters, which presents an upper bound to the length of passwords
+     that will work with LDAP authentication.
+    </para>
+   </note>
+
    <para>
     The following configuration options are used in both modes:
     <variablelist>
@@ -1765,6 +1800,21 @@ host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapse
     a single value, in which case this value will apply to all servers.
    </para>
 
+   <note>
+    <para>
+     While PostgreSQL's RADIUS authentication method does not have a defined
+     maximum password length, many client utilities (e.g.
+     <application>psql</application>) will truncate passwords provided via
+     prompt to 99 characters.  Longer passwords may be specified using
+     alternatives to password prompts such as the <filename>.pgpass</filename>
+     file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+     environment variable (<xref linkend="libpq-envars"/>).  However, it should
+     be noted that the server restricts password messages to an effective limit
+     of 995 characters, which presents an upper bound to the length of passwords
+     that will work with RADIUS authentication.
+    </para>
+   </note>
+
    <para>
     The following configuration options are supported for RADIUS:
      <variablelist>
@@ -1893,6 +1943,21 @@ host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapse
     <productname>Linux-PAM</productname> Page</ulink>.
    </para>
 
+   <note>
+    <para>
+     While PostgreSQL's PAM authentication method does not have a defined
+     maximum password length, many client utilities (e.g.
+     <application>psql</application>) will truncate passwords provided via
+     prompt to 99 characters.  Longer passwords may be specified using
+     alternatives to password prompts such as the <filename>.pgpass</filename>
+     file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+     environment variable (<xref linkend="libpq-envars"/>).  However, it should
+     be noted that the server restricts password messages to an effective limit
+     of 995 characters, which presents an upper bound to the length of passwords
+     that will work with PAM authentication.
+    </para>
+   </note>
+
    <para>
     The following configuration options are supported for PAM:
     <variablelist>
@@ -1956,6 +2021,21 @@ host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapse
     exist, and <productname>PostgreSQL</productname> will use the default login class.
    </para>
 
+   <note>
+    <para>
+     While PostgreSQL's BSD authentication method does not have a defined
+     maximum password length, many client utilities (e.g.
+     <application>psql</application>) will truncate passwords provided via
+     prompt to 99 characters.  Longer passwords may be specified using
+     alternatives to password prompts such as the <filename>.pgpass</filename>
+     file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+     environment variable (<xref linkend="libpq-envars"/>).  However, it should
+     be noted that the server restricts password messages to an effective limit
+     of 995 characters, which presents an upper bound to the length of passwords
+     that will work with BSD authentication.
+    </para>
+   </note>
+
    <note>
     <para>
      To use BSD Authentication, the PostgreSQL user account (that is, the
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 06d909e804..28d5c0e57c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1102,6 +1102,16 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <para>
        Password to be used if the server demands password authentication.
       </para>
+      <note>
+       <para>
+        The server restricts password messages to an effective limit of 995
+        characters.  While this is enough for the
+        <literal>scram-sha-256</literal> and <literal>md5</literal>
+        authentication methods, it may not be enough for passwords sent in
+        clear-text via methods such as <literal>password</literal>.  See
+        <xref linkend="client-authentication"/> for more information.
+       </para>
+      </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/clusterdb.sgml b/doc/src/sgml/ref/clusterdb.sgml
index ed343dd7da..6b41910e8c 100644
--- a/doc/src/sgml/ref/clusterdb.sgml
+++ b/doc/src/sgml/ref/clusterdb.sgml
@@ -239,6 +239,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/createdb.sgml b/doc/src/sgml/ref/createdb.sgml
index 2658efeb1a..dc995bb295 100644
--- a/doc/src/sgml/ref/createdb.sgml
+++ b/doc/src/sgml/ref/createdb.sgml
@@ -274,6 +274,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/createuser.sgml b/doc/src/sgml/ref/createuser.sgml
index 22ee99f2cc..e1db7f96e6 100644
--- a/doc/src/sgml/ref/createuser.sgml
+++ b/doc/src/sgml/ref/createuser.sgml
@@ -213,6 +213,11 @@ PostgreSQL documentation
        the password of the new user. This is not necessary if you do not plan
        on using password authentication.
        </para>
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
@@ -378,6 +383,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
    </variablelist>
diff --git a/doc/src/sgml/ref/dropdb.sgml b/doc/src/sgml/ref/dropdb.sgml
index 38f38f01ce..5e96079d0d 100644
--- a/doc/src/sgml/ref/dropdb.sgml
+++ b/doc/src/sgml/ref/dropdb.sgml
@@ -194,6 +194,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/dropuser.sgml b/doc/src/sgml/ref/dropuser.sgml
index 3d4e4b37b3..8904f68119 100644
--- a/doc/src/sgml/ref/dropuser.sgml
+++ b/doc/src/sgml/ref/dropuser.sgml
@@ -198,6 +198,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
    </variablelist>
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index c9f6ce4bb3..d97a859b53 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -641,6 +641,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
     </variablelist>
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 790e81c32c..96b16824a8 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -1172,6 +1172,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index c51a130f43..b8c489ef55 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -623,6 +623,22 @@ PostgreSQL documentation
         to be dumped.  Usually, it's better to set up a
         <filename>~/.pgpass</filename> file than to rely on manual password entry.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/pg_receivewal.sgml b/doc/src/sgml/ref/pg_receivewal.sgml
index a18ddd4bff..55c3580f04 100644
--- a/doc/src/sgml/ref/pg_receivewal.sgml
+++ b/doc/src/sgml/ref/pg_receivewal.sgml
@@ -325,6 +325,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
     </variablelist>
diff --git a/doc/src/sgml/ref/pg_recvlogical.sgml b/doc/src/sgml/ref/pg_recvlogical.sgml
index 141c5cddce..d052db8fa8 100644
--- a/doc/src/sgml/ref/pg_recvlogical.sgml
+++ b/doc/src/sgml/ref/pg_recvlogical.sgml
@@ -356,6 +356,22 @@ PostgreSQL documentation
          In some cases it is worth typing <option>-W</option> to avoid the extra
          connection attempt.
         </para>
+
+        <note>
+         <para>
+          Passwords provided via prompt will be truncated to 99 characters.
+          Longer passwords may be specified using alternatives to password
+          prompts such as the <filename>.pgpass</filename> file
+          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+          environment variable (<xref linkend="libpq-envars"/>).  However, it
+          should be noted that the server restricts password messages to an
+          effective limit of 995 characters.   While this is enough for the
+          <literal>scram-sha-256</literal> and <literal>md5</literal>
+          authentication methods, it may not be enough for passwords sent in
+          clear-text via methods such as <literal>password</literal>.  See
+          <xref linkend="client-authentication"/> for more information.
+         </para>
+        </note>
       </listitem>
      </varlistentry>
      </variablelist>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 725acb192c..726d53eba0 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -781,6 +781,22 @@
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eb9d93a168..c49e925cc5 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -526,6 +526,22 @@ EOF
        and so it affects uses of the meta-command
        <command>\connect</command> as well as the initial connection attempt.
       </para>
+
+      <note>
+       <para>
+        Passwords provided via prompt will be truncated to 99 characters.
+        Longer passwords may be specified using alternatives to password prompts
+        such as the <filename>.pgpass</filename> file
+        (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+        environment variable (<xref linkend="libpq-envars"/>).  However, it
+        should be noted that the server restricts password messages to an
+        effective limit of 995 characters.   While this is enough for the
+        <literal>scram-sha-256</literal> and <literal>md5</literal>
+        authentication methods, it may not be enough for passwords sent in
+        clear-text via methods such as <literal>password</literal>.  See
+        <xref linkend="client-authentication"/> for more information.
+       </para>
+      </note>
       </listitem>
     </varlistentry>
 
diff --git a/doc/src/sgml/ref/reindexdb.sgml b/doc/src/sgml/ref/reindexdb.sgml
index 1273dad807..ddc1b7cd56 100644
--- a/doc/src/sgml/ref/reindexdb.sgml
+++ b/doc/src/sgml/ref/reindexdb.sgml
@@ -307,6 +307,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml
index 955a17a849..ca7ff34816 100644
--- a/doc/src/sgml/ref/vacuumdb.sgml
+++ b/doc/src/sgml/ref/vacuumdb.sgml
@@ -332,6 +332,22 @@ PostgreSQL documentation
         In some cases it is worth typing <option>-W</option> to avoid the extra
         connection attempt.
        </para>
+
+       <note>
+        <para>
+         Passwords provided via prompt will be truncated to 99 characters.
+         Longer passwords may be specified using alternatives to password
+         prompts such as the <filename>.pgpass</filename> file
+         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
+         environment variable (<xref linkend="libpq-envars"/>).  However, it
+         should be noted that the server restricts password messages to an
+         effective limit of 995 characters.   While this is enough for the
+         <literal>scram-sha-256</literal> and <literal>md5</literal>
+         authentication methods, it may not be enough for passwords sent in
+         clear-text via methods such as <literal>password</literal>.  See
+         <xref linkend="client-authentication"/> for more information.
+        </para>
+       </note>
       </listitem>
      </varlistentry>
 
-- 
2.16.2

v1-0003-Increase-the-accepted-length-of-password-messages.patchapplication/octet-stream; name=v1-0003-Increase-the-accepted-length-of-password-messages.patchDownload
From d79007a80b30a1fad78ca97c0a014846cb0a7ccf Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Fri, 12 Oct 2018 17:30:08 +0000
Subject: [PATCH v1 3/3] Increase the accepted length of password messages to
 8192.

---
 doc/src/sgml/client-auth.sgml        | 10 +++++-----
 doc/src/sgml/libpq.sgml              |  2 +-
 doc/src/sgml/ref/clusterdb.sgml      |  2 +-
 doc/src/sgml/ref/createdb.sgml       |  2 +-
 doc/src/sgml/ref/createuser.sgml     |  2 +-
 doc/src/sgml/ref/dropdb.sgml         |  2 +-
 doc/src/sgml/ref/dropuser.sgml       |  2 +-
 doc/src/sgml/ref/pg_basebackup.sgml  |  2 +-
 doc/src/sgml/ref/pg_dump.sgml        |  2 +-
 doc/src/sgml/ref/pg_dumpall.sgml     |  2 +-
 doc/src/sgml/ref/pg_receivewal.sgml  |  2 +-
 doc/src/sgml/ref/pg_recvlogical.sgml |  2 +-
 doc/src/sgml/ref/pg_restore.sgml     |  2 +-
 doc/src/sgml/ref/psql-ref.sgml       |  2 +-
 doc/src/sgml/ref/reindexdb.sgml      |  2 +-
 doc/src/sgml/ref/vacuumdb.sgml       |  2 +-
 src/backend/libpq/auth.c             |  2 +-
 17 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 1ce581ae0a..96d4926155 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1007,7 +1007,7 @@ omicron         bryanh                  guest1
     </para>
     <para>
      Furthermore, it should be noted that the server restricts password messages
-     to an effective limit of 995 characters.  While this is enough for the
+     to an effective limit of 8187 characters.  While this is enough for the
      <literal>scram-sha-256</literal> and <literal>md5</literal> authentication
      methods, it may not be enough for passwords sent in clear-text via the
      method <literal>password</literal>.
@@ -1512,7 +1512,7 @@ omicron         bryanh                  guest1
      file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
      environment variable (<xref linkend="libpq-envars"/>).  However, it should
      be noted that the server restricts password messages to an effective limit
-     of 995 characters, which presents an upper bound to the length of passwords
+     of 8187 characters, which presents an upper bound to the length of passwords
      that will work with LDAP authentication.
     </para>
    </note>
@@ -1810,7 +1810,7 @@ host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapse
      file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
      environment variable (<xref linkend="libpq-envars"/>).  However, it should
      be noted that the server restricts password messages to an effective limit
-     of 995 characters, which presents an upper bound to the length of passwords
+     of 8187 characters, which presents an upper bound to the length of passwords
      that will work with RADIUS authentication.
     </para>
    </note>
@@ -1953,7 +1953,7 @@ host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapse
      file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
      environment variable (<xref linkend="libpq-envars"/>).  However, it should
      be noted that the server restricts password messages to an effective limit
-     of 995 characters, which presents an upper bound to the length of passwords
+     of 8187 characters, which presents an upper bound to the length of passwords
      that will work with PAM authentication.
     </para>
    </note>
@@ -2031,7 +2031,7 @@ host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapse
      file (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
      environment variable (<xref linkend="libpq-envars"/>).  However, it should
      be noted that the server restricts password messages to an effective limit
-     of 995 characters, which presents an upper bound to the length of passwords
+     of 8187 characters, which presents an upper bound to the length of passwords
      that will work with BSD authentication.
     </para>
    </note>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 28d5c0e57c..a696a45dbc 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1104,7 +1104,7 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </para>
       <note>
        <para>
-        The server restricts password messages to an effective limit of 995
+        The server restricts password messages to an effective limit of 8187
         characters.  While this is enough for the
         <literal>scram-sha-256</literal> and <literal>md5</literal>
         authentication methods, it may not be enough for passwords sent in
diff --git a/doc/src/sgml/ref/clusterdb.sgml b/doc/src/sgml/ref/clusterdb.sgml
index 6b41910e8c..d0eb3066d7 100644
--- a/doc/src/sgml/ref/clusterdb.sgml
+++ b/doc/src/sgml/ref/clusterdb.sgml
@@ -248,7 +248,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/createdb.sgml b/doc/src/sgml/ref/createdb.sgml
index dc995bb295..b25f0562b4 100644
--- a/doc/src/sgml/ref/createdb.sgml
+++ b/doc/src/sgml/ref/createdb.sgml
@@ -283,7 +283,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/createuser.sgml b/doc/src/sgml/ref/createuser.sgml
index e1db7f96e6..9516eda553 100644
--- a/doc/src/sgml/ref/createuser.sgml
+++ b/doc/src/sgml/ref/createuser.sgml
@@ -392,7 +392,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/dropdb.sgml b/doc/src/sgml/ref/dropdb.sgml
index 5e96079d0d..acda233446 100644
--- a/doc/src/sgml/ref/dropdb.sgml
+++ b/doc/src/sgml/ref/dropdb.sgml
@@ -203,7 +203,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/dropuser.sgml b/doc/src/sgml/ref/dropuser.sgml
index 8904f68119..0fe5cb86fe 100644
--- a/doc/src/sgml/ref/dropuser.sgml
+++ b/doc/src/sgml/ref/dropuser.sgml
@@ -207,7 +207,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index d97a859b53..e1f78bb888 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -650,7 +650,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 96b16824a8..94a1539963 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -1181,7 +1181,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index b8c489ef55..b913c0b275 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -632,7 +632,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/pg_receivewal.sgml b/doc/src/sgml/ref/pg_receivewal.sgml
index 55c3580f04..e54a44defa 100644
--- a/doc/src/sgml/ref/pg_receivewal.sgml
+++ b/doc/src/sgml/ref/pg_receivewal.sgml
@@ -334,7 +334,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/pg_recvlogical.sgml b/doc/src/sgml/ref/pg_recvlogical.sgml
index d052db8fa8..ae56dad276 100644
--- a/doc/src/sgml/ref/pg_recvlogical.sgml
+++ b/doc/src/sgml/ref/pg_recvlogical.sgml
@@ -365,7 +365,7 @@ PostgreSQL documentation
           (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
           environment variable (<xref linkend="libpq-envars"/>).  However, it
           should be noted that the server restricts password messages to an
-          effective limit of 995 characters.   While this is enough for the
+          effective limit of 8187 characters.   While this is enough for the
           <literal>scram-sha-256</literal> and <literal>md5</literal>
           authentication methods, it may not be enough for passwords sent in
           clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 726d53eba0..93f1fa2848 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -790,7 +790,7 @@
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index c49e925cc5..0dea31fb87 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -535,7 +535,7 @@ EOF
         (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
         environment variable (<xref linkend="libpq-envars"/>).  However, it
         should be noted that the server restricts password messages to an
-        effective limit of 995 characters.   While this is enough for the
+        effective limit of 8187 characters.   While this is enough for the
         <literal>scram-sha-256</literal> and <literal>md5</literal>
         authentication methods, it may not be enough for passwords sent in
         clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/reindexdb.sgml b/doc/src/sgml/ref/reindexdb.sgml
index ddc1b7cd56..84f2eed62a 100644
--- a/doc/src/sgml/ref/reindexdb.sgml
+++ b/doc/src/sgml/ref/reindexdb.sgml
@@ -316,7 +316,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml
index ca7ff34816..e0eff1abeb 100644
--- a/doc/src/sgml/ref/vacuumdb.sgml
+++ b/doc/src/sgml/ref/vacuumdb.sgml
@@ -341,7 +341,7 @@ PostgreSQL documentation
          (<xref linkend="libpq-pgpass"/>) and the <envar>PGPASSWORD</envar>
          environment variable (<xref linkend="libpq-envars"/>).  However, it
          should be noted that the server restricts password messages to an
-         effective limit of 995 characters.   While this is enough for the
+         effective limit of 8187 characters.   While this is enough for the
          <literal>scram-sha-256</literal> and <literal>md5</literal>
          authentication methods, it may not be enough for passwords sent in
          clear-text via methods such as <literal>password</literal>.  See
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 8517565535..02d77841d1 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -679,7 +679,7 @@ recv_password_packet(Port *port)
 	}
 
 	initStringInfo(&buf);
-	if (pq_getmessage(&buf, 1000))	/* receive password */
+	if (pq_getmessage(&buf, 8192))	/* receive password */
 	{
 		/* EOF - pq_getmessage already logged a suitable message */
 		pfree(buf.data);
-- 
2.16.2

v1-0001-Refactor-maximum-password-length-enforced-by-clie.patchapplication/octet-stream; name=v1-0001-Refactor-maximum-password-length-enforced-by-clie.patchDownload
From ba8fb45bfad75ebba9b22bfaf397d2647a416f8e Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Fri, 12 Oct 2018 15:31:31 +0000
Subject: [PATCH v1 1/3] Refactor maximum password length enforced by client
 utilities.

---
 contrib/oid2name/oid2name.c        |  2 +-
 contrib/vacuumlo/vacuumlo.c        |  2 +-
 src/bin/initdb/initdb.c            |  4 ++--
 src/bin/pg_basebackup/streamutil.c |  2 +-
 src/bin/pg_dump/pg_backup_db.c     |  4 ++--
 src/bin/pg_dump/pg_dumpall.c       |  2 +-
 src/bin/pgbench/pgbench.c          |  2 +-
 src/bin/psql/command.c             |  6 +++---
 src/bin/psql/startup.c             |  2 +-
 src/bin/scripts/common.c           |  2 +-
 src/bin/scripts/createuser.c       |  4 ++--
 src/common/saslprep.c              |  4 ++--
 src/include/postgres_fe.h          | 11 +++++++++++
 13 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index aa122ca0e9..08904ffd12 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -285,7 +285,7 @@ sql_conn(struct options *my_opts)
 {
 	PGconn	   *conn;
 	bool		have_password = false;
-	char		password[100];
+	char		password[PROMPT_MAX_PASSWORD_LENGTH];
 	bool		new_pass;
 	PGresult   *res;
 
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index 3075781abe..5acfa65289 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[PROMPT_MAX_PASSWORD_LENGTH];
 
 	/* Note: password can be carried over from a previous call */
 	if (param->pg_prompt == TRI_YES && !have_password)
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index ab5cb7f0c1..a9663531de 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1528,8 +1528,8 @@ setup_auth(FILE *cmdfd)
 static void
 get_su_pwd(void)
 {
-	char		pwd1[100];
-	char		pwd2[100];
+	char		pwd1[PROMPT_MAX_PASSWORD_LENGTH];
+	char		pwd2[PROMPT_MAX_PASSWORD_LENGTH];
 
 	if (pwprompt)
 	{
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 52f1e559b7..8ecb412ab8 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -51,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[PROMPT_MAX_PASSWORD_LENGTH];
 PGconn	   *conn = NULL;
 
 /*
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 5e32ee8a5b..402ee3cff1 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -126,7 +126,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
 	const char *newdb;
 	const char *newuser;
 	char	   *password;
-	char		passbuf[100];
+	char		passbuf[PROMPT_MAX_PASSWORD_LENGTH];
 	bool		new_pass;
 
 	if (!reqdb)
@@ -246,7 +246,7 @@ ConnectDatabase(Archive *AHX,
 {
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 	char	   *password;
-	char		passbuf[100];
+	char		passbuf[PROMPT_MAX_PASSWORD_LENGTH];
 	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 5176626476..0521f8c700 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -1536,7 +1536,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[PROMPT_MAX_PASSWORD_LENGTH];
 
 	if (prompt_password == TRI_YES && !have_password)
 	{
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 436764b9c9..097666169b 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1179,7 +1179,7 @@ doConnect(void)
 	PGconn	   *conn;
 	bool		new_pass;
 	static bool have_password = false;
-	static char password[100];
+	static char password[PROMPT_MAX_PASSWORD_LENGTH];
 
 	/*
 	 * 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 5b4d54a442..879fa1f494 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1790,8 +1790,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[PROMPT_MAX_PASSWORD_LENGTH];
+		char		pw2[PROMPT_MAX_PASSWORD_LENGTH];
 
 		simple_prompt("Enter new password: ", pw1, sizeof(pw1), false);
 		simple_prompt("Enter it again: ", pw2, sizeof(pw2), false);
@@ -2808,7 +2808,7 @@ copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
 static char *
 prompt_for_password(const char *username)
 {
-	char		buf[100];
+	char		buf[PROMPT_MAX_PASSWORD_LENGTH];
 
 	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 be57574cd3..dfc1377382 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -100,7 +100,7 @@ main(int argc, char *argv[])
 	struct adhoc_opts options;
 	int			successResult;
 	bool		have_password = false;
-	char		password[100];
+	char		password[PROMPT_MAX_PASSWORD_LENGTH];
 	bool		new_pass;
 
 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql"));
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index ba6120706d..0b31bf3672 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -73,7 +73,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[PROMPT_MAX_PASSWORD_LENGTH];
 
 	if (!allow_password_reuse)
 		have_password = false;
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 3420e62fdd..ced7386af8 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -66,7 +66,7 @@ main(int argc, char *argv[])
 	bool		pwprompt = false;
 	char	   *newpassword = NULL;
 	char		newuser_buf[128];
-	char		newpassword_buf[100];
+	char		newpassword_buf[PROMPT_MAX_PASSWORD_LENGTH];
 
 	/* Tri-valued variables.  */
 	enum trivalue createdb = TRI_DEFAULT,
@@ -201,7 +201,7 @@ main(int argc, char *argv[])
 
 	if (pwprompt)
 	{
-		char		pw2[100];
+		char		pw2[PROMPT_MAX_PASSWORD_LENGTH];
 
 		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 4cf574fed8..1f90df250d 100644
--- a/src/common/saslprep.c
+++ b/src/common/saslprep.c
@@ -39,7 +39,7 @@
  * 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
+#define SASLPREP_MAX_PASSWORD_LENGTH	1024
 
 /*
  * In backend, we will use palloc/pfree.  In frontend, use malloc, and
@@ -1085,7 +1085,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) > SASLPREP_MAX_PASSWORD_LENGTH)
 	{
 #ifndef FRONTEND
 		ereport(ERROR,
diff --git a/src/include/postgres_fe.h b/src/include/postgres_fe.h
index 14567953f2..0c9293f942 100644
--- a/src/include/postgres_fe.h
+++ b/src/include/postgres_fe.h
@@ -26,4 +26,15 @@
 
 #include "common/fe_memutils.h"
 
+/*
+ * Limit on the length of passwords we will try to process.  Note that this does
+ * not restrict the creation of longer passwords via commands such as CREATE
+ * ROLE and ALTER ROLE.  It merely restricts the length of passwords accepted by
+ * client utility prompts (e.g. psql).
+ *
+ * One byte is reserved at the end for the '\0' byte, so the user-facing maximum
+ * length is actually 99.
+ */
+#define PROMPT_MAX_PASSWORD_LENGTH	100
+
 #endif							/* POSTGRES_FE_H */
-- 
2.16.2

#2Stephen Frost
sfrost@snowman.net
In reply to: Bossart, Nathan (#1)
Re: Maximum password length

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

#3Isaac Morland
isaac.morland@gmail.com
In reply to: Stephen Frost (#2)
Re: Maximum password length

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.

#4Bossart, Nathan
bossartn@amazon.com
In reply to: Stephen Frost (#2)
Re: Maximum password length

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]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=eb61136dc75a76caef8460fa939244d8593100f2, 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

#5Stephen Frost
sfrost@snowman.net
In reply to: Isaac Morland (#3)
Re: Maximum password length

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

#6Bossart, Nathan
bossartn@amazon.com
In reply to: Isaac Morland (#3)
Re: Maximum password length

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

#7Stephen Frost
sfrost@snowman.net
In reply to: Bossart, Nathan (#4)
Re: Maximum password length

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

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Isaac Morland (#3)
Re: Maximum password length

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

#9Stephen Frost
sfrost@snowman.net
In reply to: Bossart, Nathan (#6)
Re: Maximum password length

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

#10Bossart, Nathan
bossartn@amazon.com
In reply to: Stephen Frost (#9)
Re: Maximum password length

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

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bossart, Nathan (#10)
Re: Maximum password length

"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

#12Bossart, Nathan
bossartn@amazon.com
In reply to: Tom Lane (#11)
Re: Maximum password length

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]https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html. 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

#13Alexander Kukushkin
cyberdemn@gmail.com
In reply to: Tom Lane (#11)
Re: Maximum password length

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

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alexander Kukushkin (#13)
1 attachment(s)
Re: Maximum password length

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]/messages/by-id/CAOhmDze1nqG2vfegpSsTFCgaiFRsqgjO6yLsbmhroz2zGmJHog@mail.gmail.com, 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]/messages/by-id/4187382.1598909041@sss.pgh.pa.us,
since it modifies the test case added there.

regards, tom lane

[1]: /messages/by-id/CAOhmDze1nqG2vfegpSsTFCgaiFRsqgjO6yLsbmhroz2zGmJHog@mail.gmail.com
[2]: /messages/by-id/4187382.1598909041@sss.pgh.pa.us

Attachments:

unify-password-length-limits-2.patchtext/x-diff; charset=us-ascii; name=unify-password-length-limits-2.patchDownload
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 here to exercise handling of long lines in the file. This very long comment is just here to exercise handling of long lines in the file. This very long comment is just here to exercise handling of long lines in the file. This very long comment is 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);
#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#14)
Re: Maximum password length

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

#16Bossart, Nathan
bossartn@amazon.com
In reply to: Tom Lane (#15)
Re: Maximum password length

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]/messages/by-id/CAOhmDze1nqG2vfegpSsTFCgaiFRsqgjO6yLsbmhroz2zGmJHog@mail.gmail.com, 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]: /messages/by-id/CAOhmDze1nqG2vfegpSsTFCgaiFRsqgjO6yLsbmhroz2zGmJHog@mail.gmail.com

#17Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#14)
Re: Maximum password length

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

#18Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#17)
Re: Maximum password length

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

#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#18)
1 attachment(s)
Re: Maximum password length

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

Attachments:

remove-password-length-limits-1.patchtext/x-diff; charset=us-ascii; name=remove-password-length-limits-1.patchDownload
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;
 
#20Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#19)
1 attachment(s)
Re: Maximum password length

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

Attachments:

remove-password-length-limits-2.patchtext/x-diff; charset=us-ascii; name=remove-password-length-limits-2.patchDownload
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;
 
#21Bossart, Nathan
bossartn@amazon.com
In reply to: Tom Lane (#20)
Re: Maximum password length

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

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bossart, Nathan (#21)
Re: Maximum password length

"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

#23Bossart, Nathan
bossartn@amazon.com
In reply to: Tom Lane (#22)
Re: Maximum password length

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

#24Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bossart, Nathan (#23)
Re: Maximum password length

"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]/messages/by-id/20200903200842.GA11952@alvherre.pgsql. 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]: /messages/by-id/20200903200842.GA11952@alvherre.pgsql

#25Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#24)
Re: Maximum password length

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