]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Lock down regression testing temporary clusters on Windows.
authorNoah Misch <noah@leadboat.com>
Thu, 18 Dec 2014 03:48:40 +0000 (22:48 -0500)
committerNoah Misch <noah@leadboat.com>
Thu, 18 Dec 2014 03:48:48 +0000 (22:48 -0500)
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

contrib/dblink/Makefile
contrib/dblink/expected/dblink.out
contrib/dblink/sql/dblink.sql
doc/src/sgml/regress.sgml
src/test/regress/pg_regress.c

index 61763a019e530aa84ce8796bf286958756878008..9a0a11c1a6a4a3905893b32192a82bdb4e293b50 100644 (file)
@@ -8,7 +8,8 @@ SHLIB_LINK = $(libpq)
 DATA_built = dblink.sql 
 DATA = uninstall_dblink.sql 
 REGRESS = paths dblink
-REGRESS_OPTS = --dbname=$(CONTRIB_TESTDB) --dlpath=$(top_builddir)/src/test/regress
+REGRESS_OPTS = --dbname=$(CONTRIB_TESTDB) --dlpath=$(top_builddir)/src/test/regress \
+       --create-role=dblink_regression_test
 EXTRA_CLEAN = sql/paths.sql expected/paths.out
 
 
index 66413a3add7b2e672272b7af9b116b65cfec8283..c1a2be9d7275d01cb6672df9c5745362459f12e7 100644 (file)
@@ -817,7 +817,6 @@ SELECT dblink_disconnect('dtest1');
 (1 row)
 
 -- test foreign data wrapper functionality
-CREATE USER dblink_regression_test;
 CREATE FOREIGN DATA WRAPPER postgresql;
 CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
 CREATE USER MAPPING FOR public SERVER fdtest;
@@ -855,7 +854,6 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
 \c - :ORIGINAL_USER
 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;
 DROP USER MAPPING FOR public SERVER fdtest;
 DROP SERVER fdtest;
 DROP FOREIGN DATA WRAPPER postgresql;
index 4adef490e751d3038535900ae6af43be4aadba9e..a3df862cd54e807c5f3d459b643df458d0727851 100644 (file)
@@ -396,7 +396,6 @@ SELECT dblink_error_message('dtest1');
 SELECT dblink_disconnect('dtest1');
 
 -- test foreign data wrapper functionality
-CREATE USER dblink_regression_test;
 
 CREATE FOREIGN DATA WRAPPER postgresql;
 CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
@@ -415,7 +414,6 @@ SELECT * FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
 \c - :ORIGINAL_USER
 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;
 DROP USER MAPPING FOR public SERVER fdtest;
 DROP SERVER fdtest;
 DROP FOREIGN DATA WRAPPER postgresql;
index 333b2159b9d9a664be7f89dd760133b76ae40ecd..ac30ba293c036b9402ca547ca76e2f5362f30c6e 100644 (file)
@@ -55,19 +55,6 @@ gmake check
    <quote>failure</> represents a serious problem.
   </para>
 
-  <warning>
-   <para>
-    On systems lacking Unix-domain sockets, notably Windows, this test method
-    starts a temporary server 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>gmake check</> on an affected
-    system shared with untrusted users.  Instead, run the tests after
-    completing the installation, as described in the next section.
-   </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
index fcf9fbf6db47a3f5d3ec051faddb88581afec7e8..5380dd21118abe96fd86ef2b8b0cf8d6e55be228 100644 (file)
@@ -102,6 +102,7 @@ static bool port_specified_by_user = false;
 static char *dlpath = PKGLIBDIR;
 static char *user = NULL;
 static _stringlist *extraroles = NULL;
+static char *config_auth_datadir = NULL;
 
 /* internal variables */
 static const char *progname;
@@ -974,6 +975,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;
+       char            username[128];
+       DWORD           sz = sizeof(username) - 1;
+       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);
+       if (!GetUserName(username, &sz))
+       {
+               fprintf(stderr, _("%s: could not get current user name: %s\n"),
+                               progname, strerror(errno));
+               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
  *
@@ -1963,6 +2108,7 @@ help(void)
        printf(_("Usage: %s [options...] [extra tests...]\n"), progname);
        printf(_("\n"));
        printf(_("Options:\n"));
+       printf(_("  --config-auth=DATADIR     update authentication settings for DATADIR\n"));
        printf(_("  --dbname=DB               use database DB (default \"regression\")\n"));
        printf(_("  --debug                   turn on debug mode in programs that are run\n"));
        printf(_("  --inputdir=DIR            take input files from DIR (default \".\")\n"));
@@ -2029,6 +2175,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                {"create-role", required_argument, NULL, 18},
                {"temp-config", required_argument, NULL, 19},
                {"use-existing", no_argument, NULL, 20},
+               {"config-auth", required_argument, NULL, 24},
                {NULL, 0, NULL, 0}
        };
 
@@ -2122,6 +2269,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        case 20:
                                use_existing = true;
                                break;
+                       case 24:
+                               config_auth_datadir = strdup(optarg);
+                               if (!config_auth_datadir)
+                               {
+                                       fprintf(stderr, _("out of memory\n"));
+                                       exit(EXIT_FAILURE);
+                               }
+                               break;
                        default:
                                /* getopt_long already emitted a complaint */
                                fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
@@ -2139,6 +2294,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)
 
                /*
@@ -2260,6 +2423,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.
                 */