Skip to content

Commit 6aa98e9

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 5c784d9 commit 6aa98e9

File tree

5 files changed

+177
-18
lines changed

5 files changed

+177
-18
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
@@ -807,7 +807,6 @@ SELECT dblink_disconnect('dtest1');
807807
(1 row)
808808

809809
-- test foreign data wrapper functionality
810-
CREATE USER dblink_regression_test;
811810
CREATE FOREIGN DATA WRAPPER postgresql;
812811
CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
813812
CREATE USER MAPPING FOR public SERVER fdtest;
@@ -845,7 +844,6 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
845844
\c - :ORIGINAL_USER
846845
REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
847846
REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_test;
848-
DROP USER dblink_regression_test;
849847
DROP USER MAPPING FOR public SERVER fdtest;
850848
DROP SERVER fdtest;
851849
DROP FOREIGN DATA WRAPPER postgresql;

contrib/dblink/sql/dblink.sql

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

385385
-- test foreign data wrapper functionality
386-
CREATE USER dblink_regression_test;
387386

388387
CREATE FOREIGN DATA WRAPPER postgresql;
389388
CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
@@ -402,7 +401,6 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
402401
\c - :ORIGINAL_USER
403402
REVOKE USAGE ON FOREIGN SERVER fdtest FROM dblink_regression_test;
404403
REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_test;
405-
DROP USER dblink_regression_test;
406404
DROP USER MAPPING FOR public SERVER fdtest;
407405
DROP SERVER fdtest;
408406
DROP FOREIGN DATA WRAPPER postgresql;

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;
@@ -979,6 +980,150 @@ initialize_environment(void)
979980
load_resultmap();
980981
}
981982

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

@@ -2153,6 +2300,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
21532300
case 23:
21542301
add_stringlist_item(&extra_install, optarg);
21552302
break;
2303+
case 24:
2304+
config_auth_datadir = strdup(optarg);
2305+
if (!config_auth_datadir)
2306+
{
2307+
fprintf(stderr, _("out of memory\n"));
2308+
exit(EXIT_FAILURE);
2309+
}
2310+
break;
21562311
default:
21572312
/* getopt_long already emitted a complaint */
21582313
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
@@ -2170,6 +2325,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
21702325
optind++;
21712326
}
21722327

2328+
if (config_auth_datadir)
2329+
{
2330+
#ifdef ENABLE_SSPI
2331+
config_sspi_auth(config_auth_datadir);
2332+
#endif
2333+
exit(0);
2334+
}
2335+
21732336
if (temp_install && !port_specified_by_user)
21742337

21752338
/*
@@ -2310,6 +2473,18 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
23102473

23112474
fclose(pg_conf);
23122475

2476+
#ifdef ENABLE_SSPI
2477+
2478+
/*
2479+
* Since we successfully used the same buffer for the much-longer
2480+
* "initdb" command, this can't truncate.
2481+
*/
2482+
snprintf(buf, sizeof(buf), "%s/data", temp_install);
2483+
config_sspi_auth(buf);
2484+
#elif !defined(HAVE_UNIX_SOCKETS)
2485+
#error Platform has no means to secure the test installation.
2486+
#endif
2487+
23132488
/*
23142489
* Check if there is a postmaster running already.
23152490
*/

0 commit comments

Comments
 (0)