makecheck-secure-v3.patch
text/plain
Filename: makecheck-secure-v3.patch
Type: text/plain
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| contrib/dblink/expected/dblink.out | 4 | 5 |
| contrib/dblink/sql/dblink.sql | 4 | 5 |
| contrib/pg_upgrade/test.sh | 35 | 2 |
| doc/src/sgml/ref/allfiles.sgml | 1 | 0 |
| doc/src/sgml/reference.sgml | 1 | 0 |
| doc/src/sgml/ref/initdb.sgml | 2 | 0 |
| doc/src/sgml/ref/pg_genpassword.sgml | 82 | 0 |
| doc/src/sgml/regress.sgml | 12 | 20 |
| src/backend/utils/adt/encode.c | 1 | 88 |
| src/bin/Makefile | 2 | 2 |
| src/bin/pg_genpassword/.gitignore | 1 | 0 |
| src/bin/pg_genpassword/Makefile | 36 | 0 |
| src/bin/pg_genpassword/nls.mk | 4 | 0 |
| src/bin/pg_genpassword/pg_genpassword.c | 87 | 0 |
| src/include/port.h | 8 | 0 |
| src/interfaces/ecpg/test/connect/test5.pgc | 10 | 8 |
| src/interfaces/ecpg/test/expected/connect-test5.c | 44 | 38 |
| src/interfaces/ecpg/test/expected/connect-test5.stderr | 13 | 5 |
| src/port/Makefile | 2 | 2 |
| src/port/pgencode.c | 120 | 0 |
| src/port/pgrand.c | 119 | 0 |
| src/test/regress/pg_regress.c | 117 | 2 |
| src/tools/msvc/Mkvcbuild.pm | 4 | 1 |
| src/tools/msvc/vcregress.pl | 30 | 2 |
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-bugs@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;