aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2016-08-08 10:07:46 -0400
committerNoah Misch <noah@leadboat.com>2016-08-08 10:07:52 -0400
commit395d565ac76b6fe5a9a97fb5e87e0d0842ba9824 (patch)
tree26918d249e5e160406f6a00e4837f4c8aae93bdc
parent0f679d2c1cb0ef5fc43133ebebf489b82b929214 (diff)
downloadpostgresql-395d565ac76b6fe5a9a97fb5e87e0d0842ba9824.tar.gz
postgresql-395d565ac76b6fe5a9a97fb5e87e0d0842ba9824.zip
Fix Windows shell argument quoting.
The incorrect quoting may have permitted arbitrary command execution. At a minimum, it gave broader control over the command line to actors supposed to have control over a single argument. Back-patch to 9.1 (all supported versions). Security: CVE-2016-5424
-rw-r--r--src/bin/pg_dump/pg_dumpall.c52
1 files changed, 47 insertions, 5 deletions
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 29278d7ec98..8ce018c5c44 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -2116,7 +2116,7 @@ doConnStrQuoting(PQExpBuffer buf, const char *str)
/*
* Append the given string to the shell command being built in the buffer,
- * with suitable shell-style quoting.
+ * with suitable shell-style quoting to create exactly one argument.
*
* Forbid LF or CR characters, which have scant practical use beyond designing
* security breaches. The Windows command shell is unusable as a conduit for
@@ -2148,8 +2148,20 @@ doShellQuoting(PQExpBuffer buf, const char *str)
}
appendPQExpBufferChar(buf, '\'');
#else /* WIN32 */
+ int backslash_run_length = 0;
- appendPQExpBufferChar(buf, '"');
+ /*
+ * A Windows system() argument experiences two layers of interpretation.
+ * First, cmd.exe interprets the string. Its behavior is undocumented,
+ * but a caret escapes any byte except LF or CR that would otherwise have
+ * special meaning. Handling of a caret before LF or CR differs between
+ * "cmd.exe /c" and other modes, and it is unusable here.
+ *
+ * Second, the new process parses its command line to construct argv (see
+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
+ * backslash-double quote sequences specially.
+ */
+ appendPQExpBufferStr(buf, "^\"");
for (p = str; *p; p++)
{
if (*p == '\n' || *p == '\r')
@@ -2160,11 +2172,41 @@ doShellQuoting(PQExpBuffer buf, const char *str)
exit(EXIT_FAILURE);
}
+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
if (*p == '"')
- appendPQExpBuffer(buf, "\\\"");
+ {
+ while (backslash_run_length)
+ {
+ appendPQExpBufferStr(buf, "^\\");
+ backslash_run_length--;
+ }
+ appendPQExpBufferStr(buf, "^\\");
+ }
+ else if (*p == '\\')
+ backslash_run_length++;
else
- appendPQExpBufferChar(buf, *p);
+ backslash_run_length = 0;
+
+ /*
+ * Decline to caret-escape the most mundane characters, to ease
+ * debugging and lest we approach the command length limit.
+ */
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9')))
+ appendPQExpBufferChar(buf, '^');
+ appendPQExpBufferChar(buf, *p);
+ }
+
+ /*
+ * Change N backslashes at end of argument to 2N backslashes, because they
+ * precede the double quote that terminates the argument.
+ */
+ while (backslash_run_length)
+ {
+ appendPQExpBufferStr(buf, "^\\");
+ backslash_run_length--;
}
- appendPQExpBufferChar(buf, '"');
+ appendPQExpBufferStr(buf, "^\"");
#endif /* WIN32 */
}