From 1572145ce8a5846df823d861b662a48c1ac1bb90 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 31f081a..5fabfda 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 c8a8c52..6b54a46 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -3243,6 +3243,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 372865d..307c3a1 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 1c3d9b3..b8c96a9 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 452ffc0..3af601f 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.10.2

