Skip to content

Commit 0046f65

Browse files
committed
Lock down regression testing temporary clusters on Windows.
Use SSPI authentication to allow connections exclusively from the OS user that launched the test suite. This closes on Windows the vulnerability that commit be76a6d closed on other platforms. Users of "make installcheck" or custom test harnesses can run "pg_regress --config-auth=DATADIR" to activate the same authentication configuration that "make check" would use. Back-patch to 9.0 (all supported versions). Security: CVE-2014-0067
1 parent d1131ae commit 0046f65

File tree

7 files changed

+200
-24
lines changed

7 files changed

+200
-24
lines changed

contrib/dblink/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ EXTENSION = dblink
1010
DATA = dblink--1.0.sql dblink--unpackaged--1.0.sql
1111

1212
REGRESS = paths dblink
13-
REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
13+
REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress \
14+
--create-role=dblink_regression_test
1415
EXTRA_CLEAN = sql/paths.sql expected/paths.out
1516

1617
# the db name is hard-coded in the tests

contrib/dblink/expected/dblink.out

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,6 @@ SELECT dblink_disconnect('dtest1');
811811
(1 row)
812812

813813
-- test foreign data wrapper functionality
814-
CREATE USER dblink_regression_test;
815814
CREATE FOREIGN DATA WRAPPER postgresql;
816815
CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
817816
CREATE USER MAPPING FOR public SERVER fdtest;
@@ -849,7 +848,6 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
849848
\c - :ORIGINAL_USER
850849
REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
851850
REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_test;
852-
DROP USER dblink_regression_test;
853851
DROP USER MAPPING FOR public SERVER fdtest;
854852
DROP SERVER fdtest;
855853
DROP FOREIGN DATA WRAPPER postgresql;

contrib/dblink/sql/dblink.sql

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,6 @@ SELECT dblink_error_message('dtest1');
387387
SELECT dblink_disconnect('dtest1');
388388

389389
-- test foreign data wrapper functionality
390-
CREATE USER dblink_regression_test;
391390

392391
CREATE FOREIGN DATA WRAPPER postgresql;
393392
CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
@@ -406,7 +405,6 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
406405
\c - :ORIGINAL_USER
407406
REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
408407
REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_test;
409-
DROP USER dblink_regression_test;
410408
DROP USER MAPPING FOR public SERVER fdtest;
411409
DROP SERVER fdtest;
412410
DROP FOREIGN DATA WRAPPER postgresql;

contrib/pg_upgrade/test.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@ set -e
1515
: ${PGPORT=50432}
1616
export PGPORT
1717

18+
# Run a given "initdb" binary and overlay the regression testing
19+
# authentication configuration.
20+
standard_initdb() {
21+
"$1"
22+
../../src/test/regress/pg_regress --config-auth "$PGDATA"
23+
}
24+
1825
# Establish how the server will listen for connections
1926
testhost=`uname -s`
2027

2128
case $testhost in
2229
MINGW*)
2330
LISTEN_ADDRESSES="localhost"
24-
PGHOST=""; unset PGHOST
31+
PGHOST=localhost
2532
;;
2633
*)
2734
LISTEN_ADDRESSES=""
@@ -47,11 +54,11 @@ case $testhost in
4754
trap 'rm -rf "$PGHOST"' 0
4855
trap 'exit 3' 1 2 13 15
4956
fi
50-
export PGHOST
5157
;;
5258
esac
5359

5460
POSTMASTER_OPTS="-F -c listen_addresses=$LISTEN_ADDRESSES -k \"$PGHOST\""
61+
export PGHOST
5562

5663
temp_root=$PWD/tmp_check
5764

@@ -117,7 +124,7 @@ mkdir "$logdir"
117124
# enable echo so the user can see what is being executed
118125
set -x
119126

120-
$oldbindir/initdb
127+
standard_initdb "$oldbindir"/initdb
121128
$oldbindir/pg_ctl start -l "$logdir/postmaster1.log" -o "$POSTMASTER_OPTS" -w
122129
if "$MAKE" -C "$oldsrc" installcheck; then
123130
pg_dumpall -f "$temp_root"/dump1.sql || pg_dumpall1_status=$?
@@ -157,7 +164,7 @@ fi
157164

158165
PGDATA=$BASE_PGDATA
159166

160-
initdb
167+
standard_initdb 'initdb'
161168

162169
pg_upgrade -d "${PGDATA}.old" -D "${PGDATA}" -b "$oldbindir" -B "$bindir"
163170

doc/src/sgml/regress.sgml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,6 @@ gmake check
5656
<quote>failure</> represents a serious problem.
5757
</para>
5858

59-
<warning>
60-
<para>
61-
On systems lacking Unix-domain sockets, notably Windows, this test method
62-
starts a temporary server configured to accept any connection originating
63-
on the local machine. Any local user can gain database superuser
64-
privileges when connecting to this server, and could in principle exploit
65-
all privileges of the operating-system user running the tests. Therefore,
66-
it is not recommended that you use <literal>gmake check</> on an affected
67-
system shared with untrusted users. Instead, run the tests after
68-
completing the installation, as described in the next section.
69-
</para>
70-
</warning>
71-
7259
<para>
7360
Because this test method runs a temporary server, it will not work
7461
if you did the build as the root user, since the server will not start as

src/test/regress/pg_regress.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ static char *dlpath = PKGLIBDIR;
105105
static char *user = NULL;
106106
static _stringlist *extraroles = NULL;
107107
static _stringlist *extra_install = NULL;
108+
static char *config_auth_datadir = NULL;
108109

109110
/* internal variables */
110111
static const char *progname;
@@ -970,6 +971,150 @@ initialize_environment(void)
970971
load_resultmap();
971972
}
972973

974+
#ifdef ENABLE_SSPI
975+
/*
976+
* Get account and domain/realm names for the current user. This is based on
977+
* pg_SSPI_recvauth(). The returned strings use static storage.
978+
*/
979+
static void
980+
current_windows_user(const char **acct, const char **dom)
981+
{
982+
static char accountname[MAXPGPATH];
983+
static char domainname[MAXPGPATH];
984+
HANDLE token;
985+
TOKEN_USER *tokenuser;
986+
DWORD retlen;
987+
DWORD accountnamesize = sizeof(accountname);
988+
DWORD domainnamesize = sizeof(domainname);
989+
SID_NAME_USE accountnameuse;
990+
991+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
992+
{
993+
fprintf(stderr,
994+
_("%s: could not open process token: error code %lu\n"),
995+
progname, GetLastError());
996+
exit(2);
997+
}
998+
999+
if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
1000+
{
1001+
fprintf(stderr,
1002+
_("%s: could not get token user size: error code %lu\n"),
1003+
progname, GetLastError());
1004+
exit(2);
1005+
}
1006+
tokenuser = malloc(retlen);
1007+
if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
1008+
{
1009+
fprintf(stderr,
1010+
_("%s: could not get token user: error code %lu\n"),
1011+
progname, GetLastError());
1012+
exit(2);
1013+
}
1014+
1015+
if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
1016+
domainname, &domainnamesize, &accountnameuse))
1017+
{
1018+
fprintf(stderr,
1019+
_("%s: could not look up account SID: error code %lu\n"),
1020+
progname, GetLastError());
1021+
exit(2);
1022+
}
1023+
1024+
free(tokenuser);
1025+
1026+
*acct = accountname;
1027+
*dom = domainname;
1028+
}
1029+
1030+
/*
1031+
* Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
1032+
* the current OS user to authenticate as the bootstrap superuser and as any
1033+
* user named in a --create-role option.
1034+
*/
1035+
static void
1036+
config_sspi_auth(const char *pgdata)
1037+
{
1038+
const char *accountname,
1039+
*domainname;
1040+
char username[128];
1041+
DWORD sz = sizeof(username) - 1;
1042+
char fname[MAXPGPATH];
1043+
int res;
1044+
FILE *hba,
1045+
*ident;
1046+
_stringlist *sl;
1047+
1048+
/*
1049+
* "username", the initdb-chosen bootstrap superuser name, may always
1050+
* match "accountname", the value SSPI authentication discovers. The
1051+
* underlying system functions do not clearly guarantee that.
1052+
*/
1053+
current_windows_user(&accountname, &domainname);
1054+
if (!GetUserName(username, &sz))
1055+
{
1056+
fprintf(stderr, _("%s: could not get current user name: %s\n"),
1057+
progname, strerror(errno));
1058+
exit(2);
1059+
}
1060+
1061+
/* Check a Write outcome and report any error. */
1062+
#define CW(cond) \
1063+
do { \
1064+
if (!(cond)) \
1065+
{ \
1066+
fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \
1067+
progname, fname, strerror(errno)); \
1068+
exit(2); \
1069+
} \
1070+
} while (0)
1071+
1072+
res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
1073+
if (res < 0 || res >= sizeof(fname) - 1)
1074+
{
1075+
/*
1076+
* Truncating this name is a fatal error, because we must not fail to
1077+
* overwrite an original trust-authentication pg_hba.conf.
1078+
*/
1079+
fprintf(stderr, _("%s: directory name too long\n"), progname);
1080+
exit(2);
1081+
}
1082+
hba = fopen(fname, "w");
1083+
if (hba == NULL)
1084+
{
1085+
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
1086+
progname, fname, strerror(errno));
1087+
exit(2);
1088+
}
1089+
CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
1090+
CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
1091+
hba) >= 0);
1092+
CW(fclose(hba) == 0);
1093+
1094+
snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
1095+
ident = fopen(fname, "w");
1096+
if (ident == NULL)
1097+
{
1098+
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
1099+
progname, fname, strerror(errno));
1100+
exit(2);
1101+
}
1102+
CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
1103+
1104+
/*
1105+
* Double-quote for the benefit of account names containing whitespace or
1106+
* '#'. Windows forbids the double-quote character itself, so don't
1107+
* bother escaping embedded double-quote characters.
1108+
*/
1109+
CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
1110+
accountname, domainname, username) >= 0);
1111+
for (sl = extraroles; sl; sl = sl->next)
1112+
CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
1113+
accountname, domainname, sl->str) >= 0);
1114+
CW(fclose(ident) == 0);
1115+
}
1116+
#endif
1117+
9731118
/*
9741119
* Issue a command via psql, connecting to the specified database
9751120
*
@@ -1969,6 +2114,7 @@ help(void)
19692114
printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
19702115
printf(_("\n"));
19712116
printf(_("Options:\n"));
2117+
printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
19722118
printf(_(" --create-role=ROLE create the specified role before testing\n"));
19732119
printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
19742120
printf(_(" --debug turn on debug mode in programs that are run\n"));
@@ -2042,6 +2188,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
20422188
{"launcher", required_argument, NULL, 21},
20432189
{"load-extension", required_argument, NULL, 22},
20442190
{"extra-install", required_argument, NULL, 23},
2191+
{"config-auth", required_argument, NULL, 24},
20452192
{NULL, 0, NULL, 0}
20462193
};
20472194

@@ -2146,6 +2293,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
21462293
case 23:
21472294
add_stringlist_item(&extra_install, optarg);
21482295
break;
2296+
case 24:
2297+
config_auth_datadir = strdup(optarg);
2298+
if (!config_auth_datadir)
2299+
{
2300+
fprintf(stderr, _("out of memory\n"));
2301+
exit(EXIT_FAILURE);
2302+
}
2303+
break;
21492304
default:
21502305
/* getopt_long already emitted a complaint */
21512306
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
@@ -2163,6 +2318,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
21632318
optind++;
21642319
}
21652320

2321+
if (config_auth_datadir)
2322+
{
2323+
#ifdef ENABLE_SSPI
2324+
config_sspi_auth(config_auth_datadir);
2325+
#endif
2326+
exit(0);
2327+
}
2328+
21662329
if (temp_install && !port_specified_by_user)
21672330

21682331
/*
@@ -2303,6 +2466,18 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
23032466

23042467
fclose(pg_conf);
23052468

2469+
#ifdef ENABLE_SSPI
2470+
2471+
/*
2472+
* Since we successfully used the same buffer for the much-longer
2473+
* "initdb" command, this can't truncate.
2474+
*/
2475+
snprintf(buf, sizeof(buf), "%s/data", temp_install);
2476+
config_sspi_auth(buf);
2477+
#elif !defined(HAVE_UNIX_SOCKETS)
2478+
#error Platform has no means to secure the test installation.
2479+
#endif
2480+
23062481
/*
23072482
* Check if there is a postmaster running already.
23082483
*/

src/tools/msvc/vcregress.pl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,15 @@ sub contribcheck
241241
exit $mstat if $mstat;
242242
}
243243

244+
# Run "initdb", then reconfigure authentication.
245+
sub standard_initdb
246+
{
247+
return (
248+
system('initdb', '-N') == 0 and system(
249+
"$topdir/$Config/pg_regress/pg_regress", '--config-auth',
250+
$ENV{PGDATA}) == 0);
251+
}
252+
244253
sub upgradecheck
245254
{
246255
my $status;
@@ -252,6 +261,7 @@ sub upgradecheck
252261
# i.e. only this version to this version check. That's
253262
# what pg_upgrade's "make check" does.
254263

264+
$ENV{PGHOST} = 'localhost';
255265
$ENV{PGPORT} ||= 50432;
256266
my $tmp_root = "$topdir/contrib/pg_upgrade/tmp_check";
257267
(mkdir $tmp_root || die $!) unless -d $tmp_root;
@@ -268,7 +278,7 @@ sub upgradecheck
268278
my $logdir = "$topdir/contrib/pg_upgrade/log";
269279
(mkdir $logdir || die $!) unless -d $logdir;
270280
print "\nRunning initdb on old cluster\n\n";
271-
system("initdb") == 0 or exit 1;
281+
standard_initdb() or exit 1;
272282
print "\nStarting old cluster\n\n";
273283
system("pg_ctl start -l $logdir/postmaster1.log -w") == 0 or exit 1;
274284
print "\nSetting up data for upgrading\n\n";
@@ -281,7 +291,7 @@ sub upgradecheck
281291
system("pg_ctl -m fast stop") == 0 or exit 1;
282292
$ENV{PGDATA} = "$data";
283293
print "\nSetting up new cluster\n\n";
284-
system("initdb") == 0 or exit 1;
294+
standard_initdb() or exit 1;
285295
print "\nRunning pg_upgrade\n\n";
286296
system("pg_upgrade -d $data.old -D $data -b $bindir -B $bindir") == 0
287297
or exit 1;

0 commit comments

Comments
 (0)