Forbid use of LF and CR characters in database and role names

Started by Michael Paquierover 9 years ago27 messages
#1Michael Paquier
michael.paquier@gmail.com
2 attachment(s)

Hi all,

As CVE-2016-5424 has put recently in light, using LF and CR in
database and role names can lead to unexpected problems in the way
they are handled in logical backups or generated command lines. There
is as well a comment in the code mentioning a potential restriction
for that, precisely in fe_utils/string_utils.c:
+ * Forbid LF or CR characters, which have scant practical use beyond designing
+ * security breaches.  The Windows command shell is unusable as a conduit for
+ * arguments containing LF or CR characters.  A future major release should
+ * reject those characters in CREATE ROLE and CREATE DATABASE, because use
+ * there eventually leads to errors here.

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend, and I am adding that to the next CF for
10.0. Attached is as well a script able to trigger those errors.
Thoughts?
--
Michael

Attachments:

forbid-cr-lf.patchinvalid/octet-stream; name=forbid-cr-lf.patchDownload
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..5746958 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -77,6 +77,7 @@ typedef struct
 } movedb_failure_params;
 
 /* non-export function prototypes */
+static void check_db_name(const char *dbname);
 static void createdb_failure_callback(int code, Datum arg);
 static void movedb(const char *dbname, const char *tblspcname);
 static void movedb_failure_callback(int code, Datum arg);
@@ -456,6 +457,9 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* do sanity checks on database name */
+	check_db_name(dbname);
+
 	/*
 	 * Check for db name conflict.  This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -745,6 +749,22 @@ check_encoding_locale_matches(int encoding, const char *collate, const char *cty
 				   pg_encoding_to_char(collate_encoding))));
 }
 
+/*
+ * Perform sanity checks on the database name.
+ */
+static void
+check_db_name(const char *dbname)
+{
+	if (strchr(dbname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot use LF character")));
+	if (strchr(dbname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot use CR character")));
+}
+
 /* Error cleanup callback for createdb */
 static void
 createdb_failure_callback(int code, Datum arg)
@@ -949,6 +969,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* check format of new name */
+	check_db_name(newname);
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index b6ea950..8954e16 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -57,6 +57,21 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
+/* Do sanity checks on role name */
+static void
+check_role_name(const char *rolname)
+{
+	if (strchr(rolname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use LF character")));
+	if (strchr(rolname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use CR character")));
+}
+
+
 /* Check if current user has createrole privileges */
 static bool
 have_createrole_privilege(void)
@@ -111,6 +126,9 @@ CreateRole(CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
+	/* sanity check for role name */
+	check_role_name(stmt->role);
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1137,6 +1155,9 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* sanity check for role name */
+	check_role_name(newname);
+
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
createddl.pltext/x-perl-script; charset=US-ASCII; name=createddl.plDownload
#2Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#1)
1 attachment(s)
Re: Forbid use of LF and CR characters in database and role names

On Fri, Aug 12, 2016 at 10:12 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend, and I am adding that to the next CF for
10.0. Attached is as well a script able to trigger those errors.
Thoughts?

I am re-sending the patch. For a reason escaping me, it is showing up
as 'invalid/octet-stream'... (Thanks Bruce for noting that)
--
Michael

Attachments:

forbid-cr-lf-v2.patchtext/plain; charset=US-ASCII; name=forbid-cr-lf-v2.patchDownload
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..5746958 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -77,6 +77,7 @@ typedef struct
 } movedb_failure_params;
 
 /* non-export function prototypes */
+static void check_db_name(const char *dbname);
 static void createdb_failure_callback(int code, Datum arg);
 static void movedb(const char *dbname, const char *tblspcname);
 static void movedb_failure_callback(int code, Datum arg);
@@ -456,6 +457,9 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* do sanity checks on database name */
+	check_db_name(dbname);
+
 	/*
 	 * Check for db name conflict.  This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -745,6 +749,22 @@ check_encoding_locale_matches(int encoding, const char *collate, const char *cty
 				   pg_encoding_to_char(collate_encoding))));
 }
 
+/*
+ * Perform sanity checks on the database name.
+ */
+static void
+check_db_name(const char *dbname)
+{
+	if (strchr(dbname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot use LF character")));
+	if (strchr(dbname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot use CR character")));
+}
+
 /* Error cleanup callback for createdb */
 static void
 createdb_failure_callback(int code, Datum arg)
@@ -949,6 +969,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* check format of new name */
+	check_db_name(newname);
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index b6ea950..8954e16 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -57,6 +57,21 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
+/* Do sanity checks on role name */
+static void
+check_role_name(const char *rolname)
+{
+	if (strchr(rolname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use LF character")));
+	if (strchr(rolname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use CR character")));
+}
+
+
 /* Check if current user has createrole privileges */
 static bool
 have_createrole_privilege(void)
@@ -111,6 +126,9 @@ CreateRole(CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
+	/* sanity check for role name */
+	check_role_name(stmt->role);
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1137,6 +1155,9 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* sanity check for role name */
+	check_role_name(newname);
+
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
#3Peter Geoghegan
pg@heroku.com
In reply to: Michael Paquier (#1)
Re: Forbid use of LF and CR characters in database and role names

I haven't looked at the patch, but offhand I wonder if it's worth
considering control characters added by unicode, if you haven't already.

--
Peter Geoghegan

#4Michael Paquier
michael.paquier@gmail.com
In reply to: Peter Geoghegan (#3)
Re: Forbid use of LF and CR characters in database and role names

On Tue, Aug 23, 2016 at 10:19 AM, Peter Geoghegan <pg@heroku.com> wrote:

I haven't looked at the patch, but offhand I wonder if it's worth
considering control characters added by unicode, if you haven't already.

There is no need to put restrictions on those I think, and they are
actually supported. Look for example at pg_upgrade/test.sh, we are
already testing them with database names :) Not BEL of course, but
that works.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Peter Geoghegan
pg@heroku.com
In reply to: Michael Paquier (#4)
Re: Forbid use of LF and CR characters in database and role names

On Mon, Aug 22, 2016 at 6:28 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

There is no need to put restrictions on those I think, and they are
actually supported.

Bi-directional text support (i.e., the use of right-to-left control
characters) is known to have security implications, FWIW. There is an
interesting discussion of the matter here:

http://www.unicode.org/reports/tr36/#Bidirectional_Text_Spoofing

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Geoghegan (#5)
Re: Forbid use of LF and CR characters in database and role names

Peter Geoghegan <pg@heroku.com> writes:

On Mon, Aug 22, 2016 at 6:28 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

There is no need to put restrictions on those I think, and they are
actually supported.

Bi-directional text support (i.e., the use of right-to-left control
characters) is known to have security implications, FWIW. There is an
interesting discussion of the matter here:

http://www.unicode.org/reports/tr36/#Bidirectional_Text_Spoofing

The problem with implementing anything like that is that it requires
assumptions about what encoding we're dealing with, which would be
entirely not based in fact. (The DB encoding is not a good guide
to what global names are encoded as, much less what encoding some
shell might think it's using.)

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Michael Paquier (#1)
Re: Forbid use of LF and CR characters in database and role names

On 8/11/16 9:12 PM, Michael Paquier wrote:

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend,

How about some documentation? I think the CREATE ROLE and CREATE
DATABASE man pages might be suitable places.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Michael Paquier
michael.paquier@gmail.com
In reply to: Peter Eisentraut (#7)
1 attachment(s)
Re: Forbid use of LF and CR characters in database and role names

On Fri, Sep 2, 2016 at 2:44 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 8/11/16 9:12 PM, Michael Paquier wrote:

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend,

How about some documentation? I think the CREATE ROLE and CREATE
DATABASE man pages might be suitable places.

Sure. What do you think about that?
+  <para>
+    Database names cannot include <literal>LF</> or <literal>CR</> 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>
-- 
Michael

Attachments:

forbid-cr-lf-v3.patchtext/x-diff; charset=US-ASCII; name=forbid-cr-lf-v3.patchDownload
diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml
index cf33746..d73dc3a 100644
--- a/doc/src/sgml/ref/create_database.sgml
+++ b/doc/src/sgml/ref/create_database.sgml
@@ -260,6 +260,13 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable>
    connection <quote>slot</> remains for the database, it is possible that
    both will fail.  Also, the limit is not enforced against superusers.
   </para>
+
+  <para>
+    Database names cannot include <literal>LF</> or <literal>CR</> 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/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index 38cd4c8..27259f2 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -404,6 +404,13 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
    <command>\password</command> that can be used to safely change the
    password later.
   </para>
+
+  <para>
+   Role names cannot include <literal>LF</> or <literal>CR</> 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/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..5746958 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -77,6 +77,7 @@ typedef struct
 } movedb_failure_params;
 
 /* non-export function prototypes */
+static void check_db_name(const char *dbname);
 static void createdb_failure_callback(int code, Datum arg);
 static void movedb(const char *dbname, const char *tblspcname);
 static void movedb_failure_callback(int code, Datum arg);
@@ -456,6 +457,9 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* do sanity checks on database name */
+	check_db_name(dbname);
+
 	/*
 	 * Check for db name conflict.  This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -745,6 +749,22 @@ check_encoding_locale_matches(int encoding, const char *collate, const char *cty
 				   pg_encoding_to_char(collate_encoding))));
 }
 
+/*
+ * Perform sanity checks on the database name.
+ */
+static void
+check_db_name(const char *dbname)
+{
+	if (strchr(dbname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot use LF character")));
+	if (strchr(dbname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot use CR character")));
+}
+
 /* Error cleanup callback for createdb */
 static void
 createdb_failure_callback(int code, Datum arg)
@@ -949,6 +969,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* check format of new name */
+	check_db_name(newname);
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index b6ea950..8954e16 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -57,6 +57,21 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
+/* Do sanity checks on role name */
+static void
+check_role_name(const char *rolname)
+{
+	if (strchr(rolname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use LF character")));
+	if (strchr(rolname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use CR character")));
+}
+
+
 /* Check if current user has createrole privileges */
 static bool
 have_createrole_privilege(void)
@@ -111,6 +126,9 @@ CreateRole(CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
+	/* sanity check for role name */
+	check_role_name(stmt->role);
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1137,6 +1155,9 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* sanity check for role name */
+	check_role_name(newname);
+
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
#9Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Michael Paquier (#1)
Re: Forbid use of LF and CR characters in database and role names

On 8/11/16 9:12 PM, Michael Paquier wrote:

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend, and I am adding that to the next CF for
10.0. Attached is as well a script able to trigger those errors.

After further review, I have my doubts about this approach.

Everything that is using appendShellString() is now going to reject LF
and CR characters, but there is no systematic way by which this is
managed, enforced, or documented. It happens that right now most of the
affected cases are user and database names, but there are others. For
example, you cannot anymore install PostgreSQL into a path containing
LF/CR, because initdb will fail when it composes the pg_ctl command line
to print out. Also, initdb will fail if the data directory name
contains LF/CR, but it creates the directory nonetheless. (Apparently,
it doesn't even clean it up.) But for example pg_ctl and pg_basebackup
and postgres itself handle all of that just fine. This is a slowly
growing mess.

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#9)
Re: Forbid use of LF and CR characters in database and role names

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 8/11/16 9:12 PM, Michael Paquier wrote:

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend, and I am adding that to the next CF for
10.0. Attached is as well a script able to trigger those errors.

After further review, I have my doubts about this approach.

Everything that is using appendShellString() is now going to reject LF
and CR characters, but there is no systematic way by which this is
managed, enforced, or documented. It happens that right now most of the
affected cases are user and database names, but there are others. For
example, you cannot anymore install PostgreSQL into a path containing
LF/CR, because initdb will fail when it composes the pg_ctl command line
to print out. Also, initdb will fail if the data directory name
contains LF/CR, but it creates the directory nonetheless. (Apparently,
it doesn't even clean it up.) But for example pg_ctl and pg_basebackup
and postgres itself handle all of that just fine. This is a slowly
growing mess.

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

I think probably a better answer is to reject bad paths earlier, eg have
initdb error out before doing anything if the proposed -D path contains
CR/LF. Given the collection of security bugs we just fixed, and the
strong likelihood that user-written scripts contain other instances of
the same type of problem, I do not think we are doing anybody any favors
if we sweat bullets to support such paths. And I'm not even convinced
that we *can* support such paths on Windows; no one was able to find a
safe shell quoting solution there.

And yeah, the docs probably need work.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#9)
Re: Forbid use of LF and CR characters in database and role names

On Tue, Sep 6, 2016 at 9:43 PM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 8/11/16 9:12 PM, Michael Paquier wrote:

Note that pg_dump[all] and pg_upgrade already have safeguards against
those things per the same routines putting quotes for execution as
commands into psql and shell. So attached is a patch to implement this
restriction in the backend, and I am adding that to the next CF for
10.0. Attached is as well a script able to trigger those errors.

After further review, I have my doubts about this approach.

Everything that is using appendShellString() is now going to reject LF
and CR characters, but there is no systematic way by which this is
managed, enforced, or documented. It happens that right now most of the
affected cases are user and database names, but there are others. For
example, you cannot anymore install PostgreSQL into a path containing
LF/CR, because initdb will fail when it composes the pg_ctl command line
to print out. Also, initdb will fail if the data directory name
contains LF/CR, but it creates the directory nonetheless. (Apparently,
it doesn't even clean it up.) But for example pg_ctl and pg_basebackup
and postgres itself handle all of that just fine. This is a slowly
growing mess.

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

If we were talking about pathnames containing spaces, I would agree,
but I've never heard of a legitimate pathname containing CR or LF. I
can't see us losing much by refusing to allow such pathnames, except
for security holes.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Michael Paquier
michael.paquier@gmail.com
In reply to: Tom Lane (#10)
2 attachment(s)
Re: Forbid use of LF and CR characters in database and role names

On Wed, Sep 7, 2016 at 2:32 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I think probably a better answer is to reject bad paths earlier, eg have
initdb error out before doing anything if the proposed -D path contains
CR/LF.

Yes, that's a bug that we had better address. It is not nice to not
clean up PGDATA in this case.

Given the collection of security bugs we just fixed, and the
strong likelihood that user-written scripts contain other instances of
the same type of problem, I do not think we are doing anybody any favors
if we sweat bullets to support such paths.

Yep. Database and role names are the two patterns defined by the user
at SQL level that can be reused by command lines, so it still seems to
me that the patch I sent is the correct call..

And I'm not even convinced
that we *can* support such paths on Windows; no one was able to find a
safe shell quoting solution there.

None has been found as far as I know, the problem gets into Windows
core with paths passed to cmd.exe which cannot handle those two
characters correctly.

And yeah, the docs probably need work.

Patch 0001 is what I have done for the database and role names. Patch
0002 adds a routine in string_utils.c to check if a string given is
valid for shell commands or not. I added a call of that to initdb.c,
which is at least what we'd want to check because it calls directly
appendShellString. Now I am a bit reluctant to spread that
elsewhere... Users would get the message only with initdb if they see
the problem. I have added a note in the page of initdb as well, but
that does not sound enough to me, we may want to add a warning in a
more common plase. Does somebody have an idea where we could add that.

Also, in 0001 I have cleared the docs to refer to newline and carriage
return characters instead of CR and LF.
--
Michael

Attachments:

0001-Forbid-newline-and-carriage-return-characters-in-dat.patchtext/x-diff; charset=US-ASCII; name=0001-Forbid-newline-and-carriage-return-characters-in-dat.patchDownload
From 016a8168b39b67a6468ee1a752ee9ec82cf3fecb Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 7 Sep 2016 16:39:57 +0900
Subject: [PATCH 1/2] Forbid newline and carriage return characters in database
 and role names

---
 doc/src/sgml/ref/create_database.sgml |  7 +++++++
 doc/src/sgml/ref/create_role.sgml     |  7 +++++++
 src/backend/commands/dbcommands.c     | 23 +++++++++++++++++++++++
 src/backend/commands/user.c           | 21 +++++++++++++++++++++
 src/fe_utils/string_utils.c           |  4 +---
 5 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml
index cf33746..2477ba7 100644
--- a/doc/src/sgml/ref/create_database.sgml
+++ b/doc/src/sgml/ref/create_database.sgml
@@ -260,6 +260,13 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable>
    connection <quote>slot</> remains for the database, it is possible that
    both will fail.  Also, the limit is not enforced against superusers.
   </para>
+
+  <para>
+    Database names 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/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index 38cd4c8..9d6926e 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -404,6 +404,13 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
    <command>\password</command> that can be used to safely change the
    password later.
   </para>
+
+  <para>
+   Role names 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/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index ef48659..22712ec 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -77,6 +77,7 @@ typedef struct
 } movedb_failure_params;
 
 /* non-export function prototypes */
+static void check_db_name(const char *dbname);
 static void createdb_failure_callback(int code, Datum arg);
 static void movedb(const char *dbname, const char *tblspcname);
 static void movedb_failure_callback(int code, Datum arg);
@@ -469,6 +470,9 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* do sanity checks on database name */
+	check_db_name(dbname);
+
 	/*
 	 * Check for db name conflict.  This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -758,6 +762,22 @@ check_encoding_locale_matches(int encoding, const char *collate, const char *cty
 				   pg_encoding_to_char(collate_encoding))));
 }
 
+/*
+ * Perform sanity checks on the database name.
+ */
+static void
+check_db_name(const char *dbname)
+{
+	if (strchr(dbname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot include newline character")));
+	if (strchr(dbname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot include carriage return character")));
+}
+
 /* Error cleanup callback for createdb */
 static void
 createdb_failure_callback(int code, Datum arg)
@@ -962,6 +982,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* check format of new name */
+	check_db_name(newname);
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 4027c89..fd737d8 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -57,6 +57,21 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
+/* Do sanity checks on role name */
+static void
+check_role_name(const char *rolname)
+{
+	if (strchr(rolname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use newline character")));
+	if (strchr(rolname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use carriage return character")));
+}
+
+
 /* Check if current user has createrole privileges */
 static bool
 have_createrole_privilege(void)
@@ -111,6 +126,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
+	/* sanity check for role name */
+	check_role_name(stmt->role);
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1150,6 +1168,9 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* sanity check for role name */
+	check_role_name(newname);
+
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 61bf9e6..1c3d9b3 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -422,9 +422,7 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
  *
  * Forbid LF or CR characters, which have scant practical use beyond designing
  * security breaches.  The Windows command shell is unusable as a conduit for
- * arguments containing LF or CR characters.  A future major release should
- * reject those characters in CREATE ROLE and CREATE DATABASE, because use
- * there eventually leads to errors here.
+ * arguments containing LF or CR characters.
  */
 void
 appendShellString(PQExpBuffer buf, const char *str)
-- 
2.10.0

0002-Ensure-clean-up-of-data-directory-even-with-restrict.patchtext/x-diff; charset=US-ASCII; name=0002-Ensure-clean-up-of-data-directory-even-with-restrict.patchDownload
From 8fbf60619e12f5cd10ba90cba59496f7c962f8c3 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 7 Sep 2016 16:55:15 +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/fe_utils/string_utils.c         | 25 +++++++++++++++++++++++++
 src/include/fe_utils/string_utils.h |  1 +
 4 files changed, 35 insertions(+)

diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 4e339ec..dc01444 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 3350e13..a2aec1b 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -3499,6 +3499,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/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.0

#13Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Robert Haas (#11)
Re: Forbid use of LF and CR characters in database and role names

On 9/6/16 1:42 PM, Robert Haas wrote:

If we were talking about pathnames containing spaces, I would agree,
but I've never heard of a legitimate pathname containing CR or LF. I
can't see us losing much by refusing to allow such pathnames, except
for security holes.

The flip side of that is that if we're doing a half-way job of only
prohibiting these characters in 67% of cases, then a new generation of
tools will be written on top of that with the assumption that these
characters cannot appear. But then those tools will be easy to break or
exploit because it's possible to sneak stuff in in creative ways. So
we're on the road to having an endless stream of "I can sneak in a CR/LF
character in here" bugs.

The current setup is more robust: We are prohibiting these characters
in specific locations where we know we can't handle them. But we don't
give any guarantees about anything else.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Noah Misch
noah@leadboat.com
In reply to: Peter Eisentraut (#13)
Re: Forbid use of LF and CR characters in database and role names

On Thu, Sep 08, 2016 at 12:12:49PM -0400, Peter Eisentraut wrote:

On 9/6/16 1:42 PM, Robert Haas wrote:

On Tue, Sep 6, 2016 at 9:43 PM, Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote:

Everything that is using appendShellString() is now going to reject LF
and CR characters, but there is no systematic way by which this is
managed, enforced, or documented.

I agree, and I think that's roughly what it deserves. Longstanding use of
MAXPGPATH is worse; we truncate silently and proceed to whatever malfunction
that entails. We do not see complaints about it. To make MAXPGPATH or LF/CR
better-managed would nominally improve PostgreSQL, but the value is low.

I discourage documenting LF/CR restrictions. For the epsilon of readers who
would benefit from this knowledge, the error message suffices. For everyone
else, it would just dilute the text. (One could argue against other parts of
our documentation on this basis, but I'm not calling for such a study. I'm
just saying that today's lack of documentation on this topic is optimal.)

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

If we were talking about pathnames containing spaces, I would agree,
but I've never heard of a legitimate pathname containing CR or LF. I
can't see us losing much by refusing to allow such pathnames, except
for security holes.

(In other words, +1 to that.)

The flip side of that is that if we're doing a half-way job of only
prohibiting these characters in 67% of cases, then a new generation of
tools will be written on top of that with the assumption that these
characters cannot appear.

There's some risk there, but it doesn't feel too likely to me. If the current
tools generation had contemplated those characters, we'd have learned of
CVE-2016-5424 earlier. Even so, ...

The current setup is more robust: We are prohibiting these characters
in specific locations where we know we can't handle them. But we don't
give any guarantees about anything else.

... I'd be fine with that conclusion.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Michael Paquier
michael.paquier@gmail.com
In reply to: Noah Misch (#14)
Re: Forbid use of LF and CR characters in database and role names

On Mon, Sep 12, 2016 at 10:01 AM, Noah Misch <noah@leadboat.com> wrote:

I discourage documenting LF/CR restrictions. For the epsilon of readers who
would benefit from this knowledge, the error message suffices. For everyone
else, it would just dilute the text. (One could argue against other parts of
our documentation on this basis, but I'm not calling for such a study. I'm
just saying that today's lack of documentation on this topic is optimal.)

Okay.

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

If we were talking about pathnames containing spaces, I would agree,
but I've never heard of a legitimate pathname containing CR or LF. I
can't see us losing much by refusing to allow such pathnames, except
for security holes.

(In other words, +1 to that.)

Yep. To be honest, I see value in preventing directly the use of
newlines and carriage returns in database and role names to avoid
users to be bitten by custom scripts, things for example written in
bash that scan manually for pg_database, pg_roles, etc. And I have
seen such things over the years. Now it is true that the safeguards in
core are proving to be enough, if only the in-core tools are used, but
that's not necessarily the case with all the things gravitating around
this community.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#15)
Re: Forbid use of LF and CR characters in database and role names

On Mon, Sep 12, 2016 at 11:38 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Mon, Sep 12, 2016 at 10:01 AM, Noah Misch <noah@leadboat.com> wrote:

I discourage documenting LF/CR restrictions. For the epsilon of readers who
would benefit from this knowledge, the error message suffices. For everyone
else, it would just dilute the text. (One could argue against other parts of
our documentation on this basis, but I'm not calling for such a study. I'm
just saying that today's lack of documentation on this topic is optimal.)

Okay.

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

If we were talking about pathnames containing spaces, I would agree,
but I've never heard of a legitimate pathname containing CR or LF. I
can't see us losing much by refusing to allow such pathnames, except
for security holes.

(In other words, +1 to that.)

Yep. To be honest, I see value in preventing directly the use of
newlines and carriage returns in database and role names to avoid
users to be bitten by custom scripts, things for example written in
bash that scan manually for pg_database, pg_roles, etc. And I have
seen such things over the years. Now it is true that the safeguards in
core are proving to be enough, if only the in-core tools are used, but
that's not necessarily the case with all the things gravitating around
this community.

And seeing nothing happening here, I still don't know what to do with
this patch. Thoughts? If we are going to do nothing I would suggest to
just remove the comment in string_utils.c saying that such LF and CR
will be unsupported in a future release and move on.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#16)
Re: Forbid use of LF and CR characters in database and role names

On Sun, Oct 2, 2016 at 10:47 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

And seeing nothing happening here, I still don't know what to do with
this patch. Thoughts? If we are going to do nothing I would suggest to
just remove the comment in string_utils.c saying that such LF and CR
will be unsupported in a future release and move on.

And so I am just marking this patch as returned with feedback.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Noah Misch
noah@leadboat.com
In reply to: Michael Paquier (#16)
Re: Forbid use of LF and CR characters in database and role names

On Sun, Oct 02, 2016 at 10:47:04PM +0900, Michael Paquier wrote:

On Mon, Sep 12, 2016 at 11:38 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Mon, Sep 12, 2016 at 10:01 AM, Noah Misch <noah@leadboat.com> wrote:

I discourage documenting LF/CR restrictions. For the epsilon of readers who
would benefit from this knowledge, the error message suffices. For everyone
else, it would just dilute the text. (One could argue against other parts of
our documentation on this basis, but I'm not calling for such a study. I'm
just saying that today's lack of documentation on this topic is optimal.)

Okay.

I think the way forward here, if any, is to work on removing these
restrictions, not to keep sprinkling them around.

If we were talking about pathnames containing spaces, I would agree,
but I've never heard of a legitimate pathname containing CR or LF. I
can't see us losing much by refusing to allow such pathnames, except
for security holes.

(In other words, +1 to that.)

Yep. To be honest, I see value in preventing directly the use of
newlines and carriage returns in database and role names to avoid
users to be bitten by custom scripts, things for example written in
bash that scan manually for pg_database, pg_roles, etc. And I have
seen such things over the years. Now it is true that the safeguards in
core are proving to be enough, if only the in-core tools are used, but
that's not necessarily the case with all the things gravitating around
this community.

And seeing nothing happening here, I still don't know what to do with
this patch. Thoughts?

I count one person disfavoring the patch concept of rejecting these characters
early, and I count two people, plus yourself as author, favoring it.
Therefore, the patch can move forward with the proposed design. The next
step, then, is for the patch to park in a CommitFest until someone volunteers
to review the implementation details.

nm

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Michael Paquier
michael.paquier@gmail.com
In reply to: Noah Misch (#18)
Re: Forbid use of LF and CR characters in database and role names

On Tue, Oct 11, 2016 at 11:00 AM, Noah Misch <noah@leadboat.com> wrote:

I count one person disfavoring the patch concept of rejecting these characters
early, and I count two people, plus yourself as author, favoring it.
Therefore, the patch can move forward with the proposed design. The next
step, then, is for the patch to park in a CommitFest until someone volunteers
to review the implementation details.

OK, thanks. I thought there were more opposition to it. The original
patches still apply so I am adding it back to the next CF:
https://commitfest.postgresql.org/11/711/
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Ideriha, Takeshi
ideriha.takeshi@jp.fujitsu.com
In reply to: Michael Paquier (#19)
Re: Forbid use of LF and CR characters in database and role names

Hi,
Here's a summary for what I tested in RHEL7.0, details follow.

[Summary]
1. apply patch and make world
-> failed because </para> was mistakenly coded <para>.

2.correct this mistake and make check-world
-> got 1 failed test: "'pg_dumpall with \n\r in database name'"
because test script cannot createdb "foo\n\rbar"

Details follows:
[1. apply patch and make world>]

I tried to make world after applying patch and there seems to be one small mistake in create_role.
===============================================================

openjade:ref/create_role.sgml:413:7:E: document type does not allow element "PARA" here; missing one of "FOOTNOTE", "ITEMIZEDLIST", "ORDEREDLIST", "VARIABLELIST", "CAUTION", "IMPORTANT", "NOTE", "TIP", "WARNING", "BLOCKQUOTE", "INFORMALEXAMPLE" start-tag
openjade:ref/create_role.sgml:414:11:E: end tag for "PARA" omitted, but OMITTAG NO was specified
openjade:ref/create_role.sgml:413:2: start tag was here
openjade:ref/create_role.sgml:414:11:E: end tag for "PARA" omitted, but OMITTAG NO was specified
openjade:ref/create_role.sgml:408:2: start tag was here
make[3]: *** [HTML.index] Error 1
make[3]: *** Deleting file `HTML.index'

===============================================================

[2.correct this mistake and make check-world]
After fixing this mistake locally by correcting <para> to end tag, postgresql and documentation were successfully made.
And also "make check" tests passed (not "make check-world").

================================================================
diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index 9d6926e..ff4b6f6 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -410,7 +410,7 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
    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>
+  </para>
  </refsect1>

<refsect1>
=================================================================

This failure seems actually caused by t/010_dump_connstr.pl line 65
"$node->run_log(['createdb', "foo\n\rbar"]);".

This means your patch works well.

Excerpt from log follows.
===============================================================
# Failed test 'pg_dumpall with \n\r in database name'
# at /home/ideriha/postgres-master/src/bin/pg_dump/../../../src/test/perl/TestLib.pm line 230.
# Looks like you failed 1 test of 14.
t/010_dump_connstr.pl ..

not ok 6 - pg_dumpall with \n\r in database name

Failed 1/14 subtests

Test Summary Report
-------------------
t/010_dump_connstr.pl (Wstat: 256 Tests: 14 Failed: 1)
Failed test: 6
Non-zero exit status: 1
Files=3, Tests=1822, 58 wallclock secs ( 0.20 usr 0.01 sys + 12.52 cusr 1.94 csys = 14.67 CPU)
Result: FAIL
=================================================================

Regards,
Ideriha, Takeshi
Fujitsu

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Michael Paquier
michael.paquier@gmail.com
In reply to: Ideriha, Takeshi (#20)
2 attachment(s)
Re: Forbid use of LF and CR characters in database and role names

On Tue, Nov 22, 2016 at 5:55 PM, Ideriha, Takeshi
<ideriha.takeshi@jp.fujitsu.com> wrote:

Here's a summary for what I tested in RHEL7.0, details follow.

Thanks for the review.

[Summary]
1. apply patch and make world
-> failed because </para> was mistakenly coded <para>.

2.correct this mistake and make check-world
-> got 1 failed test: "'pg_dumpall with \n\r in database name'"
because test script cannot createdb "foo\n\rbar"

The attached version addresses those problems. I have replaced the
test in src/bin/pg_dump/t/ by tests in src/bin/scripts/t/ to check if
the role name and database name with CR or LF fail to be created. I
have as well added a test for initdb when the data directory has an
incorrect character in 0002.
--
Michael

Attachments:

0002-Ensure-clean-up-of-data-directory-even-with-restrict.patchbinary/octet-stream; name=0002-Ensure-clean-up-of-data-directory-even-with-restrict.patchDownload
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

0001-Forbid-newline-and-carriage-return-characters-in-dat.patchbinary/octet-stream; name=0001-Forbid-newline-and-carriage-return-characters-in-dat.patchDownload
From 4b5fa316e9b7bc1b981537d9081874a17109497c Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 22 Nov 2016 22:21:36 +0900
Subject: [PATCH 1/2] Forbid newline and carriage return characters in database
 and role names

---
 doc/src/sgml/ref/create_database.sgml |  7 +++++++
 doc/src/sgml/ref/create_role.sgml     |  7 +++++++
 src/backend/commands/dbcommands.c     | 23 +++++++++++++++++++++++
 src/backend/commands/user.c           | 21 +++++++++++++++++++++
 src/bin/pg_dump/t/010_dump_connstr.pl |  9 +--------
 src/bin/scripts/t/020_createdb.pl     |  4 +++-
 src/bin/scripts/t/040_createuser.pl   |  4 +++-
 src/fe_utils/string_utils.c           |  4 +---
 8 files changed, 66 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml
index cf33746..2477ba7 100644
--- a/doc/src/sgml/ref/create_database.sgml
+++ b/doc/src/sgml/ref/create_database.sgml
@@ -260,6 +260,13 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable>
    connection <quote>slot</> remains for the database, it is possible that
    both will fail.  Also, the limit is not enforced against superusers.
   </para>
+
+  <para>
+    Database names 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/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index 38cd4c8..ff4b6f6 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -404,6 +404,13 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
    <command>\password</command> that can be used to safely change the
    password later.
   </para>
+
+  <para>
+   Role names 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/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 0919ad8..36c5fb4 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -77,6 +77,7 @@ typedef struct
 } movedb_failure_params;
 
 /* non-export function prototypes */
+static void check_db_name(const char *dbname);
 static void createdb_failure_callback(int code, Datum arg);
 static void movedb(const char *dbname, const char *tblspcname);
 static void movedb_failure_callback(int code, Datum arg);
@@ -469,6 +470,9 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* do sanity checks on database name */
+	check_db_name(dbname);
+
 	/*
 	 * Check for db name conflict.  This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -758,6 +762,22 @@ check_encoding_locale_matches(int encoding, const char *collate, const char *cty
 				   pg_encoding_to_char(collate_encoding))));
 }
 
+/*
+ * Perform sanity checks on the database name.
+ */
+static void
+check_db_name(const char *dbname)
+{
+	if (strchr(dbname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot include newline character")));
+	if (strchr(dbname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot include carriage return character")));
+}
+
 /* Error cleanup callback for createdb */
 static void
 createdb_failure_callback(int code, Datum arg)
@@ -962,6 +982,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* check format of new name */
+	check_db_name(newname);
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index adc6b99..e3519d6 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -57,6 +57,21 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
+/* Do sanity checks on role name */
+static void
+check_role_name(const char *rolname)
+{
+	if (strchr(rolname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use newline character")));
+	if (strchr(rolname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use carriage return character")));
+}
+
+
 /* Check if current user has createrole privileges */
 static bool
 have_createrole_privilege(void)
@@ -111,6 +126,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
+	/* sanity check for role name */
+	check_role_name(stmt->role);
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1150,6 +1168,9 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* sanity check for role name */
+	check_role_name(newname);
+
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl
index 2d0d1e4..475f207 100644
--- a/src/bin/pg_dump/t/010_dump_connstr.pl
+++ b/src/bin/pg_dump/t/010_dump_connstr.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 14;
+use Test::More tests => 13;
 
 # In a SQL_ASCII database, pgwin32_message_to_UTF16() needs to
 # interpret everything as UTF8.  We're going to use byte sequences
@@ -62,13 +62,6 @@ $node->command_ok(['pg_dumpall', '-r', '-f', $discard,  '--dbname',
 $node->command_ok(['pg_dumpall', '-r', '-l', 'dbname=template1'],
 				  'pg_dumpall -l accepts connection string');
 
-$node->run_log(['createdb', "foo\n\rbar"]);
-# not sufficient to use -r here
-$node->command_fails(['pg_dumpall', '-f', $discard],
-					 'pg_dumpall with \n\r in database name');
-$node->run_log(['dropdb', "foo\n\rbar"]);
-
-
 # make a table, so the parallel worker has something to dump
 $node->safe_psql($dbname1, 'CREATE TABLE t0()');
 # XXX no printed message when this fails, just SIGPIPE termination
diff --git a/src/bin/scripts/t/020_createdb.pl b/src/bin/scripts/t/020_createdb.pl
index c0f6067..765ab0e 100644
--- a/src/bin/scripts/t/020_createdb.pl
+++ b/src/bin/scripts/t/020_createdb.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 13;
+use Test::More tests => 14;
 
 program_help_ok('createdb');
 program_version_ok('createdb');
@@ -24,3 +24,5 @@ $node->issues_sql_like(
 
 $node->command_fails([ 'createdb', 'foobar1' ],
 	'fails if database already exists');
+$node->command_fails([ 'createdb', "foo\n\rbar" ],
+	'fails with \n\r in database name');
diff --git a/src/bin/scripts/t/040_createuser.pl b/src/bin/scripts/t/040_createuser.pl
index f4fc7ea..778c2fe 100644
--- a/src/bin/scripts/t/040_createuser.pl
+++ b/src/bin/scripts/t/040_createuser.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 17;
+use Test::More tests => 18;
 
 program_help_ok('createuser');
 program_version_ok('createuser');
@@ -32,3 +32,5 @@ qr/statement: CREATE ROLE regress_user3 SUPERUSER CREATEDB CREATEROLE INHERIT LO
 
 $node->command_fails([ 'createuser', 'regress_user1' ],
 	'fails if role already exists');
+$node->command_fails([ 'createuser', "foo\n\rbar" ],
+	'fails with \n\r in role name');
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 61bf9e6..1c3d9b3 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -422,9 +422,7 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
  *
  * Forbid LF or CR characters, which have scant practical use beyond designing
  * security breaches.  The Windows command shell is unusable as a conduit for
- * arguments containing LF or CR characters.  A future major release should
- * reject those characters in CREATE ROLE and CREATE DATABASE, because use
- * there eventually leads to errors here.
+ * arguments containing LF or CR characters.
  */
 void
 appendShellString(PQExpBuffer buf, const char *str)
-- 
2.10.2

#22Ideriha, Takeshi
ideriha.takeshi@jp.fujitsu.com
In reply to: Michael Paquier (#21)
Re: Forbid use of LF and CR characters in database and role names

[Summary]
1. apply patch and make world
-> failed because </para> was mistakenly coded <para>.

2.correct this mistake and make check-world
-> got 1 failed test: "'pg_dumpall with \n\r in database name'"
because test script cannot createdb "foo\n\rbar"

The attached version addresses those problems. I have replaced the test in
src/bin/pg_dump/t/ by tests in src/bin/scripts/t/ to check if the role name
and database name with CR or LF fail to be created. I have as well added a test
for initdb when the data directory has an incorrect character in 0002.

Thanks for your modification.
I applied your fixed patch and new one, and confirmed the applied source passed the tests successfully. And I also checked manually the error messages were emitted successfully when cr/lf are included in dbname or rolename or data_directory.

AFAICT, this patch satisfies the concept discussed before. So I’ve switched this patch “Ready for Committer”.

Regards,
Ideriha, Takeshi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Michael Paquier
michael.paquier@gmail.com
In reply to: Ideriha, Takeshi (#22)
Re: Forbid use of LF and CR characters in database and role names

On Fri, Nov 25, 2016 at 4:02 PM, Ideriha, Takeshi
<ideriha.takeshi@jp.fujitsu.com> wrote:

I applied your fixed patch and new one, and confirmed the applied source passed the tests successfully. And I also checked manually the error messages were emitted successfully when cr/lf are included in dbname or rolename or data_directory.

AFAICT, this patch satisfies the concept discussed before. So I’ve switched this patch “Ready for Committer”.

Thanks for the review, Ideriha-san. (See you next week perhaps?)
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#23)
Re: Forbid use of LF and CR characters in database and role names

On Fri, Nov 25, 2016 at 4:41 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Fri, Nov 25, 2016 at 4:02 PM, Ideriha, Takeshi
<ideriha.takeshi@jp.fujitsu.com> wrote:

I applied your fixed patch and new one, and confirmed the applied source passed the tests successfully. And I also checked manually the error messages were emitted successfully when cr/lf are included in dbname or rolename or data_directory.

AFAICT, this patch satisfies the concept discussed before. So I’ve switched this patch “Ready for Committer”.

Thanks for the review, Ideriha-san. (See you next week perhaps?)

Patch moved to CF 2017-01.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#24)
2 attachment(s)
Re: Forbid use of LF and CR characters in database and role names

On Tue, Nov 29, 2016 at 1:33 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Fri, Nov 25, 2016 at 4:41 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Fri, Nov 25, 2016 at 4:02 PM, Ideriha, Takeshi
<ideriha.takeshi@jp.fujitsu.com> wrote:

I applied your fixed patch and new one, and confirmed the applied source passed the tests successfully. And I also checked manually the error messages were emitted successfully when cr/lf are included in dbname or rolename or data_directory.

AFAICT, this patch satisfies the concept discussed before. So I’ve switched this patch “Ready for Committer”.

Thanks for the review, Ideriha-san. (See you next week perhaps?)

Patch moved to CF 2017-01.

And nothing has happened since, the patch rotting a bit because of a
conflict in pg_dump's TAP tests. Attached is a rebased version.
--
Michael

Attachments:

0001-Forbid-newline-and-carriage-return-characters-in-dat.patchapplication/octet-stream; name=0001-Forbid-newline-and-carriage-return-characters-in-dat.patchDownload
From 217d934d0c252e312610a21cd75fb01082bb9bad Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 1 Feb 2017 13:25:51 +0900
Subject: [PATCH 1/2] Forbid newline and carriage return characters in database
 and role names

---
 doc/src/sgml/ref/create_database.sgml |  7 +++++++
 doc/src/sgml/ref/create_role.sgml     |  7 +++++++
 src/backend/commands/dbcommands.c     | 23 +++++++++++++++++++++++
 src/backend/commands/user.c           | 21 +++++++++++++++++++++
 src/bin/pg_dump/t/010_dump_connstr.pl | 11 +----------
 src/bin/scripts/t/020_createdb.pl     |  4 +++-
 src/bin/scripts/t/040_createuser.pl   |  4 +++-
 src/fe_utils/string_utils.c           |  4 +---
 8 files changed, 66 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml
index cf33746c1e..2477ba72bd 100644
--- a/doc/src/sgml/ref/create_database.sgml
+++ b/doc/src/sgml/ref/create_database.sgml
@@ -260,6 +260,13 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable>
    connection <quote>slot</> remains for the database, it is possible that
    both will fail.  Also, the limit is not enforced against superusers.
   </para>
+
+  <para>
+    Database names 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/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index a3b8ed9ab4..28e0435731 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -398,6 +398,13 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
    <command>\password</command> that can be used to safely change the
    password later.
   </para>
+
+  <para>
+   Role names 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/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c3eb3c79df..f76af26c64 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -78,6 +78,7 @@ typedef struct
 } movedb_failure_params;
 
 /* non-export function prototypes */
+static void check_db_name(const char *dbname);
 static void createdb_failure_callback(int code, Datum arg);
 static void movedb(const char *dbname, const char *tblspcname);
 static void movedb_failure_callback(int code, Datum arg);
@@ -470,6 +471,9 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* do sanity checks on database name */
+	check_db_name(dbname);
+
 	/*
 	 * Check for db name conflict.  This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -756,6 +760,22 @@ check_encoding_locale_matches(int encoding, const char *collate, const char *cty
 				   pg_encoding_to_char(collate_encoding))));
 }
 
+/*
+ * Perform sanity checks on the database name.
+ */
+static void
+check_db_name(const char *dbname)
+{
+	if (strchr(dbname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot include newline character")));
+	if (strchr(dbname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("database name cannot include carriage return character")));
+}
+
 /* Error cleanup callback for createdb */
 static void
 createdb_failure_callback(int code, Datum arg)
@@ -976,6 +996,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* check format of new name */
+	check_db_name(newname);
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 4422fadd52..52d53e8e11 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -57,6 +57,21 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
+/* Do sanity checks on role name */
+static void
+check_role_name(const char *rolname)
+{
+	if (strchr(rolname, '\n') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use newline character")));
+	if (strchr(rolname, '\r') != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("role name cannot use carriage return character")));
+}
+
+
 /* Check if current user has createrole privileges */
 static bool
 have_createrole_privilege(void)
@@ -111,6 +126,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
+	/* sanity check for role name */
+	check_role_name(stmt->role);
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1146,6 +1164,9 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* sanity check for role name */
+	check_role_name(newname);
+
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl
index 81b5779248..74b5539691 100644
--- a/src/bin/pg_dump/t/010_dump_connstr.pl
+++ b/src/bin/pg_dump/t/010_dump_connstr.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 14;
+use Test::More tests => 13;
 
 # In a SQL_ASCII database, pgwin32_message_to_UTF16() needs to
 # interpret everything as UTF8.  We're going to use byte sequences
@@ -77,15 +77,6 @@ $node->command_ok(
 	[ 'pg_dumpall', '-r', '-l', 'dbname=template1' ],
 	'pg_dumpall -l accepts connection string');
 
-$node->run_log([ 'createdb', "foo\n\rbar" ]);
-
-# not sufficient to use -r here
-$node->command_fails(
-	[ 'pg_dumpall', '-f', $discard ],
-	'pg_dumpall with \n\r in database name');
-$node->run_log([ 'dropdb', "foo\n\rbar" ]);
-
-
 # make a table, so the parallel worker has something to dump
 $node->safe_psql($dbname1, 'CREATE TABLE t0()');
 
diff --git a/src/bin/scripts/t/020_createdb.pl b/src/bin/scripts/t/020_createdb.pl
index c0f6067a92..765ab0ea6f 100644
--- a/src/bin/scripts/t/020_createdb.pl
+++ b/src/bin/scripts/t/020_createdb.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 13;
+use Test::More tests => 14;
 
 program_help_ok('createdb');
 program_version_ok('createdb');
@@ -24,3 +24,5 @@ $node->issues_sql_like(
 
 $node->command_fails([ 'createdb', 'foobar1' ],
 	'fails if database already exists');
+$node->command_fails([ 'createdb', "foo\n\rbar" ],
+	'fails with \n\r in database name');
diff --git a/src/bin/scripts/t/040_createuser.pl b/src/bin/scripts/t/040_createuser.pl
index f4fc7ea3a4..778c2feea7 100644
--- a/src/bin/scripts/t/040_createuser.pl
+++ b/src/bin/scripts/t/040_createuser.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 17;
+use Test::More tests => 18;
 
 program_help_ok('createuser');
 program_version_ok('createuser');
@@ -32,3 +32,5 @@ qr/statement: CREATE ROLE regress_user3 SUPERUSER CREATEDB CREATEROLE INHERIT LO
 
 $node->command_fails([ 'createuser', 'regress_user1' ],
 	'fails if role already exists');
+$node->command_fails([ 'createuser', "foo\n\rbar" ],
+	'fails with \n\r in role name');
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 08fe765b5a..876c877373 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -422,9 +422,7 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
  *
  * Forbid LF or CR characters, which have scant practical use beyond designing
  * security breaches.  The Windows command shell is unusable as a conduit for
- * arguments containing LF or CR characters.  A future major release should
- * reject those characters in CREATE ROLE and CREATE DATABASE, because use
- * there eventually leads to errors here.
+ * arguments containing LF or CR characters.
  */
 void
 appendShellString(PQExpBuffer buf, const char *str)
-- 
2.11.0

0002-Ensure-clean-up-of-data-directory-even-with-restrict.patchapplication/octet-stream; name=0002-Ensure-clean-up-of-data-directory-even-with-restrict.patchDownload
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

#26Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#25)
Re: Forbid use of LF and CR characters in database and role names

On Wed, Feb 1, 2017 at 1:31 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Nov 29, 2016 at 1:33 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Patch moved to CF 2017-01.

And nothing has happened since, the patch rotting a bit because of a
conflict in pg_dump's TAP tests. Attached is a rebased version.

Moved to CF 2017-03..
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#26)
Re: Forbid use of LF and CR characters in database and role names

On Wed, Feb 1, 2017 at 1:31 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Feb 1, 2017 at 1:31 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Nov 29, 2016 at 1:33 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Patch moved to CF 2017-01.

And nothing has happened since, the patch rotting a bit because of a
conflict in pg_dump's TAP tests. Attached is a rebased version.

Moved to CF 2017-03..

And nothing has happened. This is the 4th commit fest where this patch
has been presented, and no committers have showed interest. I am
marking that as rejected, moving on to other things.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers