From 8268accf95236fd55e02b9168a64b4b472cc9ce7 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 22 Nov 2016 22:25:17 +0900
Subject: [PATCH 2/2] Ensure clean up of data directory even with restricted
 path applied

Newline and carriage return characters are forbidden by appendShellCommand,
call used when generating the command recommended to the user to launch a
server. However in the case where the data directory contained such characters
initdb did not perform any cleanup of the existing data.

Check that the data path is safe to use at a more upstream place, a point
where nothing has been created yet.
---
 doc/src/sgml/ref/initdb.sgml        |  7 +++++++
 src/bin/initdb/initdb.c             |  2 ++
 src/bin/initdb/t/001_initdb.pl      |  4 +++-
 src/fe_utils/string_utils.c         | 25 +++++++++++++++++++++++++
 src/include/fe_utils/string_utils.h |  1 +
 5 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 31f081ae7a..5fabfda23f 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -442,6 +442,13 @@ PostgreSQL documentation
    <command>initdb</command> can also be invoked via
    <command>pg_ctl initdb</command>.
   </para>
+
+  <para>
+   The data directory path cannot include newline or carriage return characters
+   as those could be at the origin of security breaches, particularly on
+   Windows where the command shell is unusable with arguments containing
+   such characters.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 443c2ee468..7cc430f686 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -3103,6 +3103,8 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
+	checkShellString(pg_data);
+
 	/* If we only need to fsync, just do it and exit */
 	if (sync_only)
 	{
diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl
index 372865d3f7..307c3a1b64 100644
--- a/src/bin/initdb/t/001_initdb.pl
+++ b/src/bin/initdb/t/001_initdb.pl
@@ -6,7 +6,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 15;
+use Test::More tests => 16;
 
 my $tempdir = TestLib::tempdir;
 my $xlogdir = "$tempdir/pgxlog";
@@ -39,3 +39,5 @@ command_ok([ 'initdb', '-N', '-T', 'german', '-X', $xlogdir, $datadir ],
 
 command_ok([ 'initdb', '-S', $datadir ], 'sync only');
 command_fails([ 'initdb', $datadir ], 'existing data directory');
+command_fails([ 'initdb', $datadir . "foo\n\rbar" ],
+	'data directory with \n\r');
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 876c877373..0265033f59 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -417,6 +417,31 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
 
 
 /*
+ * Check whether the given string is suited to be used within a shell command.
+ *
+ * The same restrictions as for appendShellString apply.
+ */
+void
+checkShellString(const char *str)
+{
+	if (strchr(str, '\n') != NULL)
+	{
+		fprintf(stderr,
+				_("string contains a newline character: \"%s\"\n"),
+				str);
+		exit(EXIT_FAILURE);
+	}
+	if (strchr(str, '\r') != NULL)
+	{
+		fprintf(stderr,
+				_("string contains a carriage return character: \"%s\"\n"),
+				str);
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+/*
  * Append the given string to the shell command being built in the buffer,
  * with shell-style quoting as needed to create exactly one argument.
  *
diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h
index 8f96a0b991..6b1ee52eb6 100644
--- a/src/include/fe_utils/string_utils.h
+++ b/src/include/fe_utils/string_utils.h
@@ -43,6 +43,7 @@ extern void appendByteaLiteral(PQExpBuffer buf,
 				   const unsigned char *str, size_t length,
 				   bool std_strings);
 
+extern void checkShellString(const char *str);
 extern void appendShellString(PQExpBuffer buf, const char *str);
 extern void appendConnStrVal(PQExpBuffer buf, const char *str);
 extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
-- 
2.11.0

