As announced with last week's releases, use of trust authentication in the
"make check" temporary database cluster makes it straightforward to hijack the
OS user account involved.  The prerequisite is another user account on the
same system.  The solution we discussed on secur...@postgresql.org was to
switch to md5 authentication with a generated, strong password.

This patch adds a pg_genpassword program, which pg_regress.c and the
pg_upgrade test drivers use to generate password files for initdb and psql.
It is essentially a portable "head -c16 /dev/urandom | xxd -p".  I have it
installing to $(bindir) for the benefit of out-of-tree test suites that create
temporary clusters.  $(pgxsdir)/$(subdir), where we install pg_regress itself,
was another candidate.  This is the first core need for high-entropy random
sequences, so I adapted px_acquire_system_randomness() to libpgport as
secure_rand_bytes().

The implementation of secure_rand_bytes() prompted the question of what to do
when there's no adequate entropy source, such as on Unix-like systems with no
/dev/urandom.  OpenSSL and OpenSSH give up; you're expected to install EGD or
PRNGD.  I made the test drivers recognize a PGTESTPWFILE environment variable.
It should name a file suitable as input to "initdb --pwfile=...".  When
present, the test drivers will skip attempting to generate a password and will
use the one in that file.  I don't see any platforms on the buildfarm that
will need to use PGTESTPWFILE, but I bet at least Tom's old HP-UX system will
need it.

The dblink and postgres_fdw tests rely on a postmaster environment such that
superuser sessions can make new bootstrap superuser connections without a
password.  pg_regress achieves this by starting the server with the same
PGPASSFILE setting as it uses for psql.  Secure "make -C contrib installcheck"
rigs will need to do something similar.

Needing some conversion from random bytes to a text password, I moved the hex
encoder from encode.c to src/port/pgencode.c.  That place will be suitable for
the base64 encoder, too, when other code needs it (pgcrypto already contains a
duplicate base64 implementation).  Though the new libpgport functions fit
better in libpgcommon, since this is slated for back-patch, I chose the
library available in all supported versions.

Peripheral to the topic at hand, I sought and did not find evidence of
contemporary systems where an unprivileged user can examine the environment
variables of another user's processes.  What's a non-ancient system for which
the warning documented for the PGPASSWORD environment variable is apropos?

Thanks,
nm

-- 
Noah Misch
EnterpriseDB                                 http://www.enterprisedb.com
diff --git a/contrib/dblink/expected/dblink.out 
b/contrib/dblink/expected/dblink.out
index f237c43..36fdf73 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -782,18 +782,17 @@ SELECT dblink_disconnect('dtest1');
 (1 row)
 
 -- test foreign data wrapper functionality
-CREATE USER dblink_regression_test;
+CREATE ROLE dblink_regression_test;
 CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw
   OPTIONS (dbname 'contrib_regression');
 CREATE USER MAPPING FOR public SERVER fdtest
   OPTIONS (server 'localhost');  -- fail, can't specify server here
 ERROR:  invalid option "server"
 HINT:  Valid options in this context are: user, password
-CREATE USER MAPPING FOR public SERVER fdtest;
+CREATE USER MAPPING FOR public SERVER fdtest OPTIONS (user :'USER');
 GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
 GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO 
dblink_regression_test;
-\set ORIGINAL_USER :USER
-\c - dblink_regression_test
+SET SESSION AUTHORIZATION dblink_regression_test;
 -- should fail
 SELECT dblink_connect('myconn', 'fdtest');
 ERROR:  password is required
@@ -821,7 +820,7 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a 
int, b text, c text[])
  10 | k | {a10,b10,c10}
 (11 rows)
 
-\c - :ORIGINAL_USER
+\c - -
 REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
 REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM 
dblink_regression_test;
 DROP USER dblink_regression_test;
diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql
index 2a10760..30396ed 100644
--- a/contrib/dblink/sql/dblink.sql
+++ b/contrib/dblink/sql/dblink.sql
@@ -359,25 +359,24 @@ SELECT dblink_error_message('dtest1');
 SELECT dblink_disconnect('dtest1');
 
 -- test foreign data wrapper functionality
-CREATE USER dblink_regression_test;
+CREATE ROLE dblink_regression_test;
 CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw
   OPTIONS (dbname 'contrib_regression');
 CREATE USER MAPPING FOR public SERVER fdtest
   OPTIONS (server 'localhost');  -- fail, can't specify server here
-CREATE USER MAPPING FOR public SERVER fdtest;
+CREATE USER MAPPING FOR public SERVER fdtest OPTIONS (user :'USER');
 
 GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
 GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO 
dblink_regression_test;
 
-\set ORIGINAL_USER :USER
-\c - dblink_regression_test
+SET SESSION AUTHORIZATION dblink_regression_test;
 -- should fail
 SELECT dblink_connect('myconn', 'fdtest');
 -- should succeed
 SELECT dblink_connect_u('myconn', 'fdtest');
 SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c 
text[]);
 
-\c - :ORIGINAL_USER
+\c - -
 REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
 REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM 
dblink_regression_test;
 DROP USER dblink_regression_test;
diff --git a/contrib/pg_upgrade/test.sh b/contrib/pg_upgrade/test.sh
index 1ef80ed..87cf01c 100644
--- a/contrib/pg_upgrade/test.sh
+++ b/contrib/pg_upgrade/test.sh
@@ -111,10 +111,43 @@ done
 EXTRA_REGRESS_OPTS="$EXTRA_REGRESS_OPTS --port=$PGPORT"
 export EXTRA_REGRESS_OPTS
 
+# Use md5 authentication; take the bootstrap superuser password from a
+# user-specified file, or generate a password.  Prepare password files
+# suitable for initdb and libpq.  A strong password prevents hostile local
+# users from connecting to the test server and using superuser-only features
+# to hijack the OS user account.
+pwdir=$temp_root/passwords
+server_pwfile=${PGTESTPWFILE-$pwdir/superuser_password}
+PGPASSFILE=$pwdir/pgpass
+export PGPASSFILE
+(
+       # Most of these commands rarely fail, but be paranoid.
+       set -e
+
+       # Since we can't readily use O_EXCL, create a directory accessible to 
the
+       # owner alone and store the files there.
+       umask 077
+       rm -rf "$pwdir"
+       mkdir "$pwdir"
+
+       if [ -n "${PGTESTPWFILE+set}" ] || pg_genpassword >"$server_pwfile"; 
then
+               :
+       else
+               cat >&2 <<EOF
+Failed to generate a strong password for the test superuser.  Set the
+PGTESTPWFILE environment variable to the absolute path of a file containing a
+strong password, which need not and should not match one you use elsewhere.
+EOF
+               exit 1
+       fi
+
+       { printf '*:*:*:*:'; cat "$server_pwfile"; } >"$PGPASSFILE"
+) || exit
+
 # enable echo so the user can see what is being executed
 set -x
 
-$oldbindir/initdb -N
+$oldbindir/initdb -N --auth=md5 --pwfile="$server_pwfile"
 $oldbindir/pg_ctl start -l "$logdir/postmaster1.log" -o "$POSTMASTER_OPTS" -w
 if "$MAKE" -C "$oldsrc" installcheck; then
        pg_dumpall -f "$temp_root"/dump1.sql || pg_dumpall1_status=$?
@@ -154,7 +187,7 @@ fi
 
 PGDATA=$BASE_PGDATA
 
-initdb -N
+initdb -N --auth=md5 --pwfile="$server_pwfile"
 
 pg_upgrade $PG_UPGRADE_OPTS -d "${PGDATA}.old" -D "${PGDATA}" -b "$oldbindir" 
-B "$bindir" -p "$PGPORT" -P "$PGPORT"
 
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index ce7a5e3..5a6ad33 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -181,6 +181,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pgCtl              SYSTEM "pg_ctl-ref.sgml">
 <!ENTITY pgDump             SYSTEM "pg_dump.sgml">
 <!ENTITY pgDumpall          SYSTEM "pg_dumpall.sgml">
+<!ENTITY pgGenpassword      SYSTEM "pg_genpassword.sgml">
 <!ENTITY pgIsready          SYSTEM "pg_isready.sgml">
 <!ENTITY pgReceivexlog      SYSTEM "pg_receivexlog.sgml">
 <!ENTITY pgResetxlog        SYSTEM "pg_resetxlog.sgml">
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 228edf7..72ef536 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -254,6 +254,8 @@ PostgreSQL documentation
        <para>
         Makes <command>initdb</command> read the database superuser's password
         from a file.  The first line of the file is taken as the password.
+        <xref linkend="app-pggenpassword"> is available for generating strong
+        passwords.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/ref/pg_genpassword.sgml 
b/doc/src/sgml/ref/pg_genpassword.sgml
new file mode 100644
index 0000000..d19b9f1
--- /dev/null
+++ b/doc/src/sgml/ref/pg_genpassword.sgml
@@ -0,0 +1,82 @@
+<!--
+doc/src/sgml/ref/pg_genpassword.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="APP-PGGENPASSWORD">
+ <refmeta>
+  <refentrytitle>pg_genpassword</refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>Application</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pg_genpassword</refname>
+  <refpurpose>generate a strong user password</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="app-pggenpassword">
+  <primary>pg_genpassword</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pg_genpassword</command>
+   <arg choice="opt"><replaceable class="parameter">option</replaceable></arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-APP-PGGENPASSWORD-1">
+  <title>
+   Description
+  </title>
+  <para>
+   <command>pg_genpassword</> generates a password from system-provided
+   entropy and prints it to standard output.  The password is not conducive to
+   human memorization.  It suits machine use, particularly automated test
+   suites.  <command>pg_genpassword</> exits with status 2 if it fails due to
+   the absence of any supported entropy source.  For any other failure cause,
+   it exits with status 1.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Options</title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+       <term><option>-V</></term>
+       <term><option>--version</></term>
+       <listitem>
+       <para>
+       Print the <application>pg_genpassword</application> version and exit.
+       </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry>
+       <term><option>-?</></term>
+       <term><option>--help</></term>
+       <listitem>
+       <para>
+       Show help about <application>pg_genpassword</application> command line
+       arguments, and exit.
+       </para>
+       </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="app-initdb"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 87e8e9e..a1dcae2 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -252,6 +252,7 @@
   </partintro>
 
    &initdb;
+   &pgGenpassword;
    &pgControldata;
    &pgCtl;
    &pgResetxlog;
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml
index 16b3621..3db8cf8 100644
--- a/doc/src/sgml/regress.sgml
+++ b/doc/src/sgml/regress.sgml
@@ -56,26 +56,6 @@ make check
    <quote>failure</> represents a serious problem.
   </para>
 
-  <warning>
-   <para>
-    This test method starts a temporary server, which is configured to accept
-    any connection originating on the local machine.  Any local user can gain
-    database superuser privileges when connecting to this server, and could
-    in principle exploit all privileges of the operating-system user running
-    the tests.  Therefore, it is not recommended that you use <literal>make
-    check</> on machines shared with untrusted users.  Instead, run the tests
-    after completing the installation, as described in the next section.
-   </para>
-
-   <para>
-    On Unix-like machines, this danger can be avoided if the temporary
-    server's socket file is made inaccessible to other users, for example
-    by running the tests in a protected chroot.  On Windows, the temporary
-    server opens a locally-accessible TCP socket, so filesystem protections
-    cannot help.
-   </para>
-  </warning>
-
    <para>
     Because this test method runs a temporary server, it will not work
     if you did the build as the root user, since the server will not start as
@@ -111,6 +91,18 @@ make MAX_CONNECTIONS=10 check
 </screen>
     runs no more than ten tests concurrently.
    </para>
+
+   <para>
+    To protect your operating system user account, the test driver generates a
+    strong password for the bootstrap superuser account of the temporary
+    database cluster.  If your platform lacks a recognized source of
+    high-entropy bytes, such as <filename>/dev/urandom</>, <literal>make
+    check</> will fail at that step.  To work around this problem, store a
+    strong password alone in a text file and set the <envar>PGTESTPWFILE</>
+    environment variable to the location of that file.  This variable can also
+    be used on systems having a recognized entropy source to avoid draining
+    the system entropy pool for password generation.
+   </para>
   </sect2>
 
   <sect2>
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 46993ba..adcd8f3 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -106,96 +106,9 @@ binary_decode(PG_FUNCTION_ARGS)
 
 
 /*
- * HEX
+ * HEX from src/port/pgencode.c
  */
 
-static const char hextbl[] = "0123456789abcdef";
-
-static const int8 hexlookup[128] = {
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-};
-
-unsigned
-hex_encode(const char *src, unsigned len, char *dst)
-{
-       const char *end = src + len;
-
-       while (src < end)
-       {
-               *dst++ = hextbl[(*src >> 4) & 0xF];
-               *dst++ = hextbl[*src & 0xF];
-               src++;
-       }
-       return len * 2;
-}
-
-static inline char
-get_hex(char c)
-{
-       int                     res = -1;
-
-       if (c > 0 && c < 127)
-               res = hexlookup[(unsigned char) c];
-
-       if (res < 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("invalid hexadecimal digit: \"%c\"", 
c)));
-
-       return (char) res;
-}
-
-unsigned
-hex_decode(const char *src, unsigned len, char *dst)
-{
-       const char *s,
-                          *srcend;
-       char            v1,
-                               v2,
-                          *p;
-
-       srcend = src + len;
-       s = src;
-       p = dst;
-       while (s < srcend)
-       {
-               if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
-               {
-                       s++;
-                       continue;
-               }
-               v1 = get_hex(*s++) << 4;
-               if (s >= srcend)
-                       ereport(ERROR,
-                                       
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                 errmsg("invalid hexadecimal data: odd number 
of digits")));
-
-               v2 = get_hex(*s++);
-               *p++ = v1 | v2;
-       }
-
-       return p - dst;
-}
-
-static unsigned
-hex_enc_len(const char *src, unsigned srclen)
-{
-       return srclen << 1;
-}
-
-static unsigned
-hex_dec_len(const char *src, unsigned srclen)
-{
-       return srclen >> 1;
-}
-
 /*
  * BASE64
  */
diff --git a/src/bin/Makefile b/src/bin/Makefile
index f03cc42..cf4358d 100644
--- a/src/bin/Makefile
+++ b/src/bin/Makefile
@@ -13,8 +13,8 @@ subdir = src/bin
 top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS = initdb pg_ctl pg_dump \
-       psql scripts pg_config pg_controldata pg_resetxlog pg_basebackup
+SUBDIRS = initdb pg_ctl pg_dump psql scripts pg_config pg_controldata \
+       pg_resetxlog pg_basebackup pg_genpassword
 
 ifeq ($(PORTNAME), win32)
 SUBDIRS += pgevent
diff --git a/src/bin/pg_genpassword/.gitignore 
b/src/bin/pg_genpassword/.gitignore
new file mode 100644
index 0000000..a310f5b
--- /dev/null
+++ b/src/bin/pg_genpassword/.gitignore
@@ -0,0 +1 @@
+/pg_genpassword
diff --git a/src/bin/pg_genpassword/Makefile b/src/bin/pg_genpassword/Makefile
new file mode 100644
index 0000000..fd6c57d
--- /dev/null
+++ b/src/bin/pg_genpassword/Makefile
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/bin/pg_genpassword
+#
+# Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/bin/pg_genpassword/Makefile
+#
+#-------------------------------------------------------------------------
+
+PGFILEDESC = "pg_genpassword - generate a strong user password"
+PGAPPICON=win32
+
+subdir = src/bin/pg_genpassword
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS= pg_genpassword.o $(WIN32RES)
+
+all: pg_genpassword
+
+pg_genpassword: $(OBJS) | submake-libpgport
+       $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+
+install: all installdirs
+       $(INSTALL_PROGRAM) pg_genpassword$(X) 
'$(DESTDIR)$(bindir)/pg_genpassword$(X)'
+
+installdirs:
+       $(MKDIR_P) '$(DESTDIR)$(bindir)'
+
+uninstall:
+       rm -f '$(DESTDIR)$(bindir)/pg_genpassword$(X)'
+
+clean distclean maintainer-clean:
+       rm -f pg_genpassword$(X) $(OBJS)
diff --git a/src/bin/pg_genpassword/nls.mk b/src/bin/pg_genpassword/nls.mk
new file mode 100644
index 0000000..64981eb
--- /dev/null
+++ b/src/bin/pg_genpassword/nls.mk
@@ -0,0 +1,4 @@
+# src/bin/pg_genpassword/nls.mk
+CATALOG_NAME     = pg_genpassword
+AVAIL_LANGUAGES  =
+GETTEXT_FILES    = pg_genpassword.c ../../port/exec.c
diff --git a/src/bin/pg_genpassword/pg_genpassword.c 
b/src/bin/pg_genpassword/pg_genpassword.c
new file mode 100644
index 0000000..a1ee5f9
--- /dev/null
+++ b/src/bin/pg_genpassword/pg_genpassword.c
@@ -0,0 +1,87 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_genpassword.c - generate a strong user password
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *               src/bin/pg_genpassword/pg_genpassword.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+const char *progname;
+
+static void
+usage(void)
+{
+       printf(_("%s generates a strong user password.\n\n"),
+                  progname);
+       printf(_("Usage:\n"));
+       printf(_("  %s [OPTION]...\n"), progname);
+       printf(_("\nOptions:\n"));
+       printf(_("  -V, --version          output version information, then 
exit\n"));
+       printf(_("  -?, --help             show this help, then exit\n"));
+       printf(_("\nReport bugs to <pgsql-b...@postgresql.org>.\n"));
+}
+
+int
+main(int argc, char **argv)
+{
+       char            entropy[16];
+       unsigned        hex_len;
+       char       *password;
+
+       progname = get_progname(argv[0]);
+       set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_genpassword"));
+
+       if (argc > 1)
+       {
+               if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 
0)
+               {
+                       usage();
+                       exit(0);
+               }
+               else if (strcmp(argv[1], "-V") == 0
+                                || strcmp(argv[1], "--version") == 0)
+               {
+                       puts("pg_genpassword (PostgreSQL) " PG_VERSION);
+                       exit(0);
+               }
+               else
+               {
+                       fprintf(stderr,
+                               _("%s: too many command-line arguments (first 
is \"%s\")\n"),
+                                       progname, argv[1]);
+                       fprintf(stderr, _("Try \"%s --help\" for more 
information.\n"),
+                                       progname);
+                       exit(1);
+               }
+       }
+
+
+       /*
+        * Generate a sequence of 16 secure random bytes, hex-encode them, and
+        * print the result.
+        */
+       if (!secure_rand_bytes((unsigned char *) entropy, sizeof(entropy)))
+       {
+               fprintf(stderr, _("%s: could not gather system entropy\n"), 
progname);
+               exit(2);
+       }
+
+       hex_len = hex_enc_len(entropy, sizeof(entropy));
+       password = malloc(hex_len + 1);
+       if (password == NULL)
+       {
+               fprintf(stderr, _("%s: out of memory\n"), progname);
+               exit(1);
+       }
+       hex_encode(entropy, sizeof(entropy), password);
+       password[hex_len] = '\0';
+
+       puts(password);
+
+       return 0;
+}
diff --git a/src/include/port.h b/src/include/port.h
index aeb7754..f5662c1 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -373,6 +373,8 @@ extern double pg_erand48(unsigned short xseed[3]);
 extern long pg_lrand48(void);
 extern void pg_srand48(long seed);
 
+extern bool secure_rand_bytes(unsigned char *buf, int count);
+
 #ifndef HAVE_FLS
 extern int     fls(int mask);
 #endif
@@ -462,6 +464,12 @@ extern int pg_check_dir(const char *dir);
 /* port/pgmkdirp.c */
 extern int     pg_mkdir_p(char *path, int omode);
 
+/* port/pgencode.c */
+extern unsigned hex_encode(const char *src, unsigned len, char *dst);
+extern unsigned hex_decode(const char *src, unsigned len, char *dst);
+extern unsigned hex_enc_len(const char *src, unsigned srclen);
+extern unsigned hex_dec_len(const char *src, unsigned srclen);
+
 /* port/pqsignal.c */
 typedef void (*pqsigfunc) (int signo);
 extern pqsigfunc pqsignal(int signo, pqsigfunc func);
diff --git a/src/interfaces/ecpg/test/connect/test5.pgc 
b/src/interfaces/ecpg/test/connect/test5.pgc
index d3efecb..5ba59eb 100644
--- a/src/interfaces/ecpg/test/connect/test5.pgc
+++ b/src/interfaces/ecpg/test/connect/test5.pgc
@@ -21,7 +21,9 @@ exec sql end declare section;
        ECPGdebug(1, stderr);
 
        exec sql connect to connectdb as main;
+       exec sql alter user connectdb ENCRYPTED PASSWORD 'insecure';
        exec sql alter user connectuser ENCRYPTED PASSWORD 'connectpw';
+       exec sql commit;
        exec sql disconnect;  /* <-- "main" not specified */
 
        strcpy(db, "connectdb");
@@ -38,28 +40,28 @@ exec sql end declare section;
        exec sql connect to 'connectdb' as main;
        exec sql disconnect main;
 
-       exec sql connect to as main user connectdb;
+       exec sql connect to as main user connectdb/insecure;
        exec sql disconnect main;
 
-       exec sql connect to connectdb as main user connectuser/connectdb;
+       exec sql connect to connectdb as main user connectuser/connectpw;
        exec sql disconnect main;
 
-       exec sql connect to unix:postgresql://localhost/connectdb as main user 
connectuser;
+       exec sql connect to unix:postgresql://localhost/connectdb as main user 
connectuser/connectpw;
        exec sql disconnect main;
 
-       exec sql connect to "unix:postgresql://localhost/connectdb" as main 
user connectuser;
+       exec sql connect to "unix:postgresql://localhost/connectdb" as main 
user connectuser/connectpw;
        exec sql disconnect main;
 
-       exec sql connect to 'unix:postgresql://localhost/connectdb' as main 
user :user;
+       exec sql connect to 'unix:postgresql://localhost/connectdb' as main 
user :user USING "connectpw";
        exec sql disconnect main;
 
-       exec sql connect to 
unix:postgresql://localhost/connectdb?connect_timeout=14&client_encoding=latin1 
as main user connectuser;
+       exec sql connect to 
unix:postgresql://localhost/connectdb?connect_timeout=14&client_encoding=latin1 
as main user connectuser/connectpw;
        exec sql disconnect main;
 
-       exec sql connect to "unix:postgresql://200.46.204.71/connectdb" as main 
user connectuser;
+       exec sql connect to "unix:postgresql://200.46.204.71/connectdb" as main 
user connectuser/connectpw;
        exec sql disconnect main;
 
-       exec sql connect to unix:postgresql://localhost/ as main user connectdb;
+       exec sql connect to unix:postgresql://localhost/ as main user connectdb 
IDENTIFIED BY insecure;
        exec sql disconnect main;
 
        /* connect twice */
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.c 
b/src/interfaces/ecpg/test/expected/connect-test5.c
index a8f79f9..79decd3 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.c
+++ b/src/interfaces/ecpg/test/expected/connect-test5.c
@@ -43,113 +43,119 @@ main(void)
        { ECPGconnect(__LINE__, 0, "connectdb" , NULL, NULL , "main", 0); }
 #line 23 "test5.pgc"
 
-       { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user 
connectuser encrypted password 'connectpw'", ECPGt_EOIT, ECPGt_EORT);}
+       { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user connectdb 
encrypted password 'insecure'", ECPGt_EOIT, ECPGt_EORT);}
 #line 24 "test5.pgc"
 
-       { ECPGdisconnect(__LINE__, "CURRENT");}
+       { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "alter user 
connectuser encrypted password 'connectpw'", ECPGt_EOIT, ECPGt_EORT);}
 #line 25 "test5.pgc"
+
+       { ECPGtrans(__LINE__, NULL, "commit");}
+#line 26 "test5.pgc"
+
+       { ECPGdisconnect(__LINE__, "CURRENT");}
+#line 27 "test5.pgc"
   /* <-- "main" not specified */
 
        strcpy(db, "connectdb");
        strcpy(id, "main");
        { ECPGconnect(__LINE__, 0, db , NULL, NULL , id, 0); }
-#line 29 "test5.pgc"
+#line 31 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, id);}
-#line 30 "test5.pgc"
+#line 32 "test5.pgc"
 
 
        { ECPGconnect(__LINE__, 0, "connectdb" , NULL, NULL , "main", 0); }
-#line 32 "test5.pgc"
+#line 34 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 33 "test5.pgc"
+#line 35 "test5.pgc"
 
 
        { ECPGconnect(__LINE__, 0, "connectdb" , NULL, NULL , "main", 0); }
-#line 35 "test5.pgc"
+#line 37 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 36 "test5.pgc"
+#line 38 "test5.pgc"
 
 
        { ECPGconnect(__LINE__, 0, "connectdb" , NULL, NULL , "main", 0); }
-#line 38 "test5.pgc"
+#line 40 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 39 "test5.pgc"
+#line 41 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "" , "connectdb" , NULL , "main", 0); }
-#line 41 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "" , "connectdb" , "insecure" , "main", 0); }
+#line 43 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 42 "test5.pgc"
+#line 44 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "connectdb" , "connectuser" , "connectdb" , 
"main", 0); }
-#line 44 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "connectdb" , "connectuser" , "connectpw" , 
"main", 0); }
+#line 46 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 45 "test5.pgc"
+#line 47 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/connectdb" , 
"connectuser" , NULL , "main", 0); }
-#line 47 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/connectdb" , 
"connectuser" , "connectpw" , "main", 0); }
+#line 49 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 48 "test5.pgc"
+#line 50 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/connectdb" , 
"connectuser" , NULL , "main", 0); }
-#line 50 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/connectdb" , 
"connectuser" , "connectpw" , "main", 0); }
+#line 52 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 51 "test5.pgc"
+#line 53 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/connectdb" , 
user , NULL , "main", 0); }
-#line 53 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/connectdb" , 
user , "connectpw" , "main", 0); }
+#line 55 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 54 "test5.pgc"
+#line 56 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, 
"unix:postgresql://localhost/connectdb?connect_timeout=14 & 
client_encoding=latin1" , "connectuser" , NULL , "main", 0); }
-#line 56 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, 
"unix:postgresql://localhost/connectdb?connect_timeout=14 & 
client_encoding=latin1" , "connectuser" , "connectpw" , "main", 0); }
+#line 58 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 57 "test5.pgc"
+#line 59 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "unix:postgresql://200.46.204.71/connectdb" 
, "connectuser" , NULL , "main", 0); }
-#line 59 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "unix:postgresql://200.46.204.71/connectdb" 
, "connectuser" , "connectpw" , "main", 0); }
+#line 61 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 60 "test5.pgc"
+#line 62 "test5.pgc"
 
 
-       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/" , "connectdb" 
, NULL , "main", 0); }
-#line 62 "test5.pgc"
+       { ECPGconnect(__LINE__, 0, "unix:postgresql://localhost/" , "connectdb" 
, "insecure" , "main", 0); }
+#line 64 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 63 "test5.pgc"
+#line 65 "test5.pgc"
 
 
        /* connect twice */
        { ECPGconnect(__LINE__, 0, "connectdb" , NULL, NULL , "main", 0); }
-#line 66 "test5.pgc"
+#line 68 "test5.pgc"
 
        { ECPGconnect(__LINE__, 0, "connectdb" , NULL, NULL , "main", 0); }
-#line 67 "test5.pgc"
+#line 69 "test5.pgc"
 
        { ECPGdisconnect(__LINE__, "main");}
-#line 68 "test5.pgc"
+#line 70 "test5.pgc"
 
 
        /* not connected */
        { ECPGdisconnect(__LINE__, "nonexistant");}
-#line 71 "test5.pgc"
+#line 73 "test5.pgc"
 
 
        return (0);
diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr 
b/src/interfaces/ecpg/test/expected/connect-test5.stderr
index 9c8dbf2..c856960 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr
@@ -2,12 +2,20 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database connectdb on <DEFAULT> port <DEFAULT>  
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: query: alter user connectuser encrypted 
password 'connectpw'; with 0 parameter(s) on connection main
+[NO_PID]: ecpg_execute on line 24: query: alter user connectdb encrypted 
password 'insecure'; with 0 parameter(s) on connection main
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 24: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_process_output on line 24: OK: ALTER ROLE
 [NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 25: query: alter user connectuser encrypted 
password 'connectpw'; with 0 parameter(s) on connection main
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 25: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 25: OK: ALTER ROLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 26: action "commit"; connection "main"
+[NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database connectdb on <DEFAULT> port <DEFAULT>  
@@ -50,11 +58,11 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGconnect: non-localhost access via sockets on line 59
+[NO_PID]: ECPGconnect: non-localhost access via sockets on line 61
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -402 on line 59: could not connect to database 
"connectdb" on line 59
+[NO_PID]: raising sqlcode -402 on line 61: could not connect to database 
"connectdb" on line 61
 [NO_PID]: sqlca: code: -402, state: 08001
-[NO_PID]: raising sqlcode -220 on line 60: connection "main" does not exist on 
line 60
+[NO_PID]: raising sqlcode -220 on line 62: connection "main" does not exist on 
line 62
 [NO_PID]: sqlca: code: -220, state: 08003
 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT>  
for user connectdb
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -66,5 +74,5 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection main closed
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: raising sqlcode -220 on line 71: connection "nonexistant" does not 
exist on line 71
+[NO_PID]: raising sqlcode -220 on line 73: connection "nonexistant" does not 
exist on line 73
 [NO_PID]: sqlca: code: -220, state: 08003
diff --git a/src/port/Makefile b/src/port/Makefile
index 1be4ff5..2a356ec 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -31,8 +31,8 @@ override CPPFLAGS := -I$(top_builddir)/src/port -DFRONTEND 
$(CPPFLAGS)
 LIBS += $(PTHREAD_LIBS)
 
 OBJS = $(LIBOBJS) chklocale.o dirmod.o erand48.o fls.o inet_net_ntop.o \
-       noblock.o path.o pgcheckdir.o pg_crc.o pgmkdirp.o pgsleep.o \
-       pgstrcasecmp.o pqsignal.o \
+       noblock.o path.o pgcheckdir.o pg_crc.o pgencode.o pgmkdirp.o \
+       pgrand.o pgsleep.o pgstrcasecmp.o pqsignal.o \
        qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o
 
 # foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
diff --git a/src/port/pgencode.c b/src/port/pgencode.c
new file mode 100644
index 0000000..313af7c
--- /dev/null
+++ b/src/port/pgencode.c
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgencode.c
+ *       Data encode/decode algorithms used in both frontend and backend code
+ *
+ * Copyright (c) 2001-2014, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *       src/port/pgencode.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+/*
+ * On success, decode functions return the number of bytes written.  In the
+ * backend, they raise errors using ereport(ERROR).  In frontend programs,
+ * they instead return -1.     Other functions defined in this file never fail.
+ */
+#ifdef FRONTEND
+#define ereport(elevel, rest) return -1
+#endif
+
+
+/*
+ * HEX
+ */
+
+static const char hextbl[] = "0123456789abcdef";
+
+static const int8 hexlookup[128] = {
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+unsigned
+hex_encode(const char *src, unsigned len, char *dst)
+{
+       const char *end = src + len;
+
+       while (src < end)
+       {
+               *dst++ = hextbl[(*src >> 4) & 0xF];
+               *dst++ = hextbl[*src & 0xF];
+               src++;
+       }
+       return len * 2;
+}
+
+static inline char
+get_hex(char c)
+{
+       int                     res = -1;
+
+       if (c > 0 && c < 127)
+               res = hexlookup[(unsigned char) c];
+
+       if (res < 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("invalid hexadecimal digit: \"%c\"", 
c)));
+
+       return (char) res;
+}
+
+unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+       const char *s,
+                          *srcend;
+       char            v1,
+                               v2,
+                          *p;
+
+       srcend = src + len;
+       s = src;
+       p = dst;
+       while (s < srcend)
+       {
+               if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+               {
+                       s++;
+                       continue;
+               }
+               v1 = get_hex(*s++) << 4;
+               if (s >= srcend)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                 errmsg("invalid hexadecimal data: odd number 
of digits")));
+
+               v2 = get_hex(*s++);
+               *p++ = v1 | v2;
+       }
+
+       return p - dst;
+}
+
+unsigned
+hex_enc_len(const char *src, unsigned srclen)
+{
+       return srclen << 1;
+}
+
+unsigned
+hex_dec_len(const char *src, unsigned srclen)
+{
+       return srclen >> 1;
+}
diff --git a/src/port/pgrand.c b/src/port/pgrand.c
new file mode 100644
index 0000000..604f6d2
--- /dev/null
+++ b/src/port/pgrand.c
@@ -0,0 +1,119 @@
+/*
+ * pgrand.c
+ *             Cryptographically secure pseudo-random numbers
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.     IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * src/port/pgrand.c
+ */
+
+#include "c.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef USE_SSL
+#include <openssl/rand.h>
+#endif
+
+#if !defined(USE_SSL) && !defined(WIN32)
+/* Wrapper for read(2) that never returns short. */
+static int
+safe_read(int fd, void *buf, size_t count)
+{
+       int                     done = 0;
+       char       *p = buf;
+       int                     res;
+
+       while (count)
+       {
+               res = read(fd, p, count);
+               if (res <= 0)
+               {
+                       if (errno == EINTR)
+                               continue;
+                       return res;
+               }
+               p += res;
+               done += res;
+               count -= res;
+       }
+       return done;
+}
+#endif
+
+/*
+ * secure_rand_bytes: retrieve an arbitrary quantity of random bytes
+ *
+ * Current callers need no more than 16 bytes per process, so seeding a PRNG
+ * costs as much as simply using the OS entropy directly.  When available, use
+ * the OpenSSL PRNG; if for no other reason, it supports a wider range of
+ * entropy sources.  In the absence of OpenSSL, retrieve bytes directly from
+ * an OS source.  If we ever add a high-volume caller, consider importing
+ * pgcrypto's PRNG for use in non-SSL builds.
+ *
+ * We never fall back to emitting low-entropy sequences.  Callers should do so
+ * where acceptable.
+ */
+bool
+secure_rand_bytes(unsigned char *buf, int count)
+{
+#if defined(USE_SSL)
+       return RAND_bytes(buf, count) == 1;
+#elif defined(WIN32)
+       int                     res;
+       HCRYPTPROV      h = 0;
+
+       res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+                                                         (CRYPT_VERIFYCONTEXT 
| CRYPT_MACHINE_KEYSET));
+       if (!res)
+               res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+                          CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | 
CRYPT_NEWKEYSET);
+       if (!res)
+               return false;
+
+       res = CryptGenRandom(h, count, buf);
+       if (!res)
+               return false;
+
+       CryptReleaseContext(h, 0);
+       return true;
+#else
+       int                     fd;
+       int                     res;
+
+       fd = open("/dev/urandom", O_RDONLY, 0);
+       if (fd == -1)
+       {
+               fd = open("/dev/random", O_RDONLY, 0);
+               if (fd == -1)
+                       return false;
+       }
+       res = safe_read(fd, buf, count);
+       close(fd);
+       return res == count;
+#endif
+}
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 3a49244..4724b43 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -19,6 +19,7 @@
 #include "pg_regress.h"
 
 #include <ctype.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <signal.h>
@@ -92,6 +93,7 @@ static char *encoding = NULL;
 static _stringlist *schedulelist = NULL;
 static _stringlist *extra_tests = NULL;
 static char *temp_install = NULL;
+static char *server_pwfile = NULL;
 static char *temp_config = NULL;
 static char *top_builddir = NULL;
 static bool nolocale = false;
@@ -682,6 +684,114 @@ add_to_path(const char *pathname, char separator, const 
char *addval)
 }
 
 /*
+ * Take the bootstrap superuser password from a user-specified file, or
+ * generate a password.  Prepare password files suitable for initdb and libpq.
+ * A strong password prevents hostile local users from connecting to the test
+ * server and using superuser-only features to hijack the OS user account.
+ *
+ * The ability to specify a password is mostly for the benefit of systems
+ * lacking the infrastructure for us to generate one.  Folks could do so for
+ * other reasons, such as to relieve pressure on system entropy.
+ */
+static void
+initialize_password(void)
+{
+       char            file_buf[MAXPGPATH * 2],
+                               password_buf[MAXPGPATH],
+                               pgpass_buf[MAXPGPATH + 32];
+       int                     fd;
+       int                     len_pgpass;
+
+       server_pwfile = getenv("PGTESTPWFILE");
+       if (server_pwfile != NULL)
+       {
+               int                     len;
+
+               /* Read the password so we can create a PGPASSFILE. */
+               fd = open(server_pwfile, O_RDONLY);
+               if (fd < 0
+                       || (len = read(fd, password_buf, sizeof(password_buf))) 
< 1
+                       || close(fd) != 0)
+               {
+                       fprintf(stderr, _("%s: could not read file \"%s\": 
%s\n"),
+                                       progname, server_pwfile, 
strerror(errno));
+                       exit(2);
+               }
+
+               if (len >= sizeof(password_buf))
+               {
+                       fprintf(stderr,
+                                       _("%s: password file \"%s\" must be 
shorter than %u bytes\n"),
+                                       progname, server_pwfile, (unsigned) 
sizeof(password_buf));
+                       exit(2);
+               }
+
+               password_buf[len] = '\0';
+       }
+       else
+       {
+               FILE       *child;
+               int                     len;
+
+               /* Generate a password. */
+               snprintf(file_buf, sizeof(file_buf),
+                                SYSTEMQUOTE "\"%s/pg_genpassword\"" 
SYSTEMQUOTE, bindir);
+               child = popen(file_buf, "r");
+
+               if (child == NULL
+                       || fgets(password_buf, sizeof(password_buf), child) == 
NULL
+                       || (len = strlen(password_buf)) < 32
+                       || pclose(child) != 0)
+               {
+                       fputs(_("Failed to generate a strong password for the 
test superuser.  Set the\n"
+                                       "PGTESTPWFILE environment variable to 
the absolute path of a file containing a\n"
+                                       "strong password, which need not and 
should not match one you use elsewhere.\n"),
+                                 stderr);
+                       exit(2);
+               }
+
+               /* Write the file for "initdb --pwfile=..." */
+               snprintf(file_buf, sizeof(file_buf),
+                                "%s/superuser_password", temp_install);
+               server_pwfile = strdup(file_buf);
+
+               /* XXX should we worry about configurations that ignore O_EXCL? 
*/
+               fd = open(server_pwfile, O_WRONLY | O_CREAT | O_EXCL,
+                                 S_IRUSR | S_IWUSR);
+               if (fd < 0
+                       || write(fd, password_buf, len) != len
+                       || close(fd) != 0)
+               {
+                       fprintf(stderr, _("%s: could not write file \"%s\": 
%s\n"),
+                                       progname, server_pwfile, 
strerror(errno));
+                       exit(2);
+               }
+       }
+
+       /*
+        * Write the PGPASSFILE.  We don't escape backslashes or colons, so
+        * user-supplied passwords bearing those characters will not work.
+        * Hackers can cope with that.
+        */
+       snprintf(file_buf, sizeof(file_buf), "%s/pgpass", temp_install);
+       doputenv("PGPASSFILE", file_buf);
+
+       len_pgpass = snprintf(pgpass_buf, sizeof(pgpass_buf),
+                                                 "*:*:*:*:%s", password_buf);
+       Assert(len_pgpass < sizeof(pgpass_buf));
+
+       fd = open(file_buf, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+       if (fd < 0
+               || write(fd, pgpass_buf, len_pgpass) != len_pgpass
+               || close(fd) != 0)
+       {
+               fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
+                               progname, file_buf, strerror(errno));
+               exit(2);
+       }
+}
+
+/*
  * Prepare environment variables for running regression tests
  */
 static void
@@ -2152,11 +2262,16 @@ regression_main(int argc, char *argv[], init_function 
ifunc, test_function tfunc
                        }
                }
 
+               /* generate a password for the bootstrap superuser */
+               initialize_password();
+
                /* initdb */
                header(_("initializing database system"));
                snprintf(buf, sizeof(buf),
-                                SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L 
\"%s\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
-                                bindir, temp_install, datadir,
+                                SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L 
\"%s\" "
+                                "--auth=md5 --pwfile=\"%s\" --noclean 
--nosync%s%s "
+                                "> \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
+                                bindir, temp_install, datadir, server_pwfile,
                                 debug ? " --debug" : "",
                                 nolocale ? " --no-locale" : "",
                                 outputdir);
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 308a4b4..36369e2 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -68,7 +68,8 @@ sub mkvcbuild
          chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
          srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
          erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
-         pgcheckdir.c pg_crc.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c
+         pgcheckdir.c pg_crc.c pgencode.c pgmkdirp.c
+         pgrand.c pgsleep.c pgstrcasecmp.c pqsignal.c
          qsort.c qsort_arg.c quotes.c
          sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
          win32env.c win32error.c win32setlocale.c);
@@ -375,6 +376,8 @@ sub mkvcbuild
        $pgreceivexlog->AddFile('src\bin\pg_basebackup\pg_receivexlog.c');
        $pgreceivexlog->AddLibrary('ws2_32.lib');
 
+       my $pggenpassword = AddSimpleFrontend('pg_genpassword');
+
        my $pgconfig = AddSimpleFrontend('pg_config');
 
        my $pgcontrol = AddSimpleFrontend('pg_controldata');
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index 447b4a1..8a6b9e9 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -7,7 +7,9 @@ use strict;
 our $config;
 
 use Cwd;
+use Fcntl qw(:DEFAULT :mode);
 use File::Copy;
+use IO::File;
 
 use Install qw(Install);
 
@@ -236,6 +238,21 @@ sub contribcheck
        exit $mstat if $mstat;
 }
 
+# Write the given content to a given file, creating the file as accessible to
+# its owner alone.  Die on any error.
+sub write_sensitive_file
+{
+       my ($file, $content) = @_;
+
+       my $fh =
+         IO::File->new($file, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)
+         || die qq{Could not open file "$file" for writing: $!};
+       $fh->print($content)
+         || die qq{Could not write to file "$file": $!};
+       $fh->close
+         || die qq{Could not close file "$file": $!};
+}
+
 sub upgradecheck
 {
        my $status;
@@ -259,12 +276,23 @@ sub upgradecheck
        my ($bindir, $libdir, $oldsrc, $newsrc) =
          ("$tmp_install/bin", "$tmp_install/lib", $topdir, $topdir);
        $ENV{PATH} = "$bindir;$ENV{PATH}";
+
+       # Generate a password for the bootstrap superuser.  Since pg_genpassword
+       # is expected to work on all supported Windows configurations, don't
+       # bother supporting the PGTESTPWFILE override.
+       my $pwfile = "$tmp_root/superuser_password";
+       $ENV{PGPASSFILE} = "$tmp_root/pgpass";
+       my $password = `pg_genpassword`;
+       die "Failed to generate strong password" if $? || length $password < 32;
+       write_sensitive_file($pwfile, $password);
+       write_sensitive_file($ENV{PGPASSFILE}, "*:*:*:*:$password");
+
        my $data = "$tmp_root/data";
        $ENV{PGDATA} = "$data.old";
        my $logdir = "$topdir/contrib/pg_upgrade/log";
        (mkdir $logdir || die $!) unless -d $logdir;
        print "\nRunning initdb on old cluster\n\n";
-       system("initdb") == 0 or exit 1;
+       system('initdb', '--auth=md5', "--pwfile=$pwfile") == 0 or exit 1;
        print "\nStarting old cluster\n\n";
        system("pg_ctl start -l $logdir/postmaster1.log -w") == 0 or exit 1;
        print "\nSetting up data for upgrading\n\n";
@@ -278,7 +306,7 @@ sub upgradecheck
        system("pg_ctl -m fast stop") == 0 or exit 1;
        $ENV{PGDATA} = "$data";
        print "\nSetting up new cluster\n\n";
-       system("initdb") == 0 or exit 1;
+       system('initdb', '--auth=md5', "--pwfile=$pwfile") == 0 or exit 1;
        print "\nRunning pg_upgrade\n\n";
        system("pg_upgrade -d $data.old -D $data -b $bindir -B $bindir") == 0
          or exit 1;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to