aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2014-12-17 22:48:40 -0500
committerNoah Misch <noah@leadboat.com>2014-12-17 22:48:40 -0500
commitf6dc6dd5ba54d52c0733aaafc50da2fbaeabb8b0 (patch)
tree4cba95a8ca5f305feac60ead92f8fdd3519311aa /src/test
parentfc2ac1fb41c2defb8caf825781af75db158fb7a9 (diff)
downloadpostgresql-f6dc6dd5ba54d52c0733aaafc50da2fbaeabb8b0.tar.gz
postgresql-f6dc6dd5ba54d52c0733aaafc50da2fbaeabb8b0.zip
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 be76a6d39e2832d4b88c0e1cc381aa44a7f86881 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
Diffstat (limited to 'src/test')
-rw-r--r--src/test/perl/TestLib.pm11
-rw-r--r--src/test/regress/pg_regress.c171
2 files changed, 181 insertions, 1 deletions
diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm
index 46a8bece1e5..57abdb92bf4 100644
--- a/src/test/perl/TestLib.pm
+++ b/src/test/perl/TestLib.pm
@@ -7,6 +7,7 @@ use Exporter 'import';
our @EXPORT = qw(
tempdir
tempdir_short
+ standard_initdb
start_test_server
restart_test_server
psql
@@ -69,6 +70,14 @@ sub tempdir_short
return File::Temp::tempdir(CLEANUP => 1);
}
+sub standard_initdb
+{
+ my $pgdata = shift;
+ system_or_bail("initdb -D '$pgdata' -A trust -N >/dev/null");
+ system_or_bail("$ENV{top_srcdir}/src/test/regress/pg_regress",
+ '--config-auth', $pgdata);
+}
+
my ($test_server_datadir, $test_server_logfile);
sub start_test_server
@@ -78,7 +87,7 @@ sub start_test_server
my $tempdir_short = tempdir_short;
- system "initdb -D '$tempdir'/pgdata -A trust -N >/dev/null";
+ standard_initdb "$tempdir/pgdata";
$ret = system 'pg_ctl', '-D', "$tempdir/pgdata", '-s', '-w', '-l',
"$tempdir/logfile", '-o',
"--fsync=off -k $tempdir_short --listen-addresses='' --log-statement=all",
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 27c46abc96a..cb092f9d821 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -29,6 +29,7 @@
#include <sys/resource.h>
#endif
+#include "common/username.h"
#include "getopt_long.h"
#include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
#include "pg_config_paths.h"
@@ -104,6 +105,7 @@ static char *dlpath = PKGLIBDIR;
static char *user = NULL;
static _stringlist *extraroles = NULL;
static _stringlist *extra_install = NULL;
+static char *config_auth_datadir = NULL;
/* internal variables */
static const char *progname;
@@ -965,6 +967,150 @@ initialize_environment(void)
load_resultmap();
}
+#ifdef ENABLE_SSPI
+/*
+ * Get account and domain/realm names for the current user. This is based on
+ * pg_SSPI_recvauth(). The returned strings use static storage.
+ */
+static void
+current_windows_user(const char **acct, const char **dom)
+{
+ static char accountname[MAXPGPATH];
+ static char domainname[MAXPGPATH];
+ HANDLE token;
+ TOKEN_USER *tokenuser;
+ DWORD retlen;
+ DWORD accountnamesize = sizeof(accountname);
+ DWORD domainnamesize = sizeof(domainname);
+ SID_NAME_USE accountnameuse;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
+ {
+ fprintf(stderr,
+ _("%s: could not open process token: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+
+ if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
+ {
+ fprintf(stderr,
+ _("%s: could not get token user size: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+ tokenuser = malloc(retlen);
+ if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
+ {
+ fprintf(stderr,
+ _("%s: could not get token user: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+
+ if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
+ domainname, &domainnamesize, &accountnameuse))
+ {
+ fprintf(stderr,
+ _("%s: could not look up account SID: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+
+ free(tokenuser);
+
+ *acct = accountname;
+ *dom = domainname;
+}
+
+/*
+ * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
+ * the current OS user to authenticate as the bootstrap superuser and as any
+ * user named in a --create-role option.
+ */
+static void
+config_sspi_auth(const char *pgdata)
+{
+ const char *accountname,
+ *domainname;
+ const char *username;
+ char *errstr;
+ char fname[MAXPGPATH];
+ int res;
+ FILE *hba,
+ *ident;
+ _stringlist *sl;
+
+ /*
+ * "username", the initdb-chosen bootstrap superuser name, may always
+ * match "accountname", the value SSPI authentication discovers. The
+ * underlying system functions do not clearly guarantee that.
+ */
+ current_windows_user(&accountname, &domainname);
+ username = get_user_name(&errstr);
+ if (username == NULL)
+ {
+ fprintf(stderr, "%s: %s\n", progname, errstr);
+ exit(2);
+ }
+
+ /* Check a Write outcome and report any error. */
+#define CW(cond) \
+ do { \
+ if (!(cond)) \
+ { \
+ fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \
+ progname, fname, strerror(errno)); \
+ exit(2); \
+ } \
+ } while (0)
+
+ res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
+ if (res < 0 || res >= sizeof(fname) - 1)
+ {
+ /*
+ * Truncating this name is a fatal error, because we must not fail to
+ * overwrite an original trust-authentication pg_hba.conf.
+ */
+ fprintf(stderr, _("%s: directory name too long\n"), progname);
+ exit(2);
+ }
+ hba = fopen(fname, "w");
+ if (hba == NULL)
+ {
+ fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
+ progname, fname, strerror(errno));
+ exit(2);
+ }
+ CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
+ CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
+ hba) >= 0);
+ CW(fclose(hba) == 0);
+
+ snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
+ ident = fopen(fname, "w");
+ if (ident == NULL)
+ {
+ fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
+ progname, fname, strerror(errno));
+ exit(2);
+ }
+ CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
+
+ /*
+ * Double-quote for the benefit of account names containing whitespace or
+ * '#'. Windows forbids the double-quote character itself, so don't
+ * bother escaping embedded double-quote characters.
+ */
+ CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
+ accountname, domainname, username) >= 0);
+ for (sl = extraroles; sl; sl = sl->next)
+ CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
+ accountname, domainname, sl->str) >= 0);
+ CW(fclose(ident) == 0);
+}
+#endif
+
/*
* Issue a command via psql, connecting to the specified database
*
@@ -1957,6 +2103,7 @@ help(void)
printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
printf(_("\n"));
printf(_("Options:\n"));
+ printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
printf(_(" --create-role=ROLE create the specified role before testing\n"));
printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
printf(_(" --debug turn on debug mode in programs that are run\n"));
@@ -2023,6 +2170,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
{"launcher", required_argument, NULL, 21},
{"load-extension", required_argument, NULL, 22},
{"extra-install", required_argument, NULL, 23},
+ {"config-auth", required_argument, NULL, 24},
{NULL, 0, NULL, 0}
};
@@ -2137,6 +2285,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
case 23:
add_stringlist_item(&extra_install, optarg);
break;
+ case 24:
+ config_auth_datadir = pstrdup(optarg);
+ break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
@@ -2154,6 +2305,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
optind++;
}
+ if (config_auth_datadir)
+ {
+#ifdef ENABLE_SSPI
+ config_sspi_auth(config_auth_datadir);
+#endif
+ exit(0);
+ }
+
if (temp_install && !port_specified_by_user)
/*
@@ -2298,6 +2457,18 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
fclose(pg_conf);
+#ifdef ENABLE_SSPI
+
+ /*
+ * Since we successfully used the same buffer for the much-longer
+ * "initdb" command, this can't truncate.
+ */
+ snprintf(buf, sizeof(buf), "%s/data", temp_install);
+ config_sspi_auth(buf);
+#elif !defined(HAVE_UNIX_SOCKETS)
+#error Platform has no means to secure the test installation.
+#endif
+
/*
* Check if there is a postmaster running already.
*/