Skip to content

Commit 395d565

Browse files
committed
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
1 parent 0f679d2 commit 395d565

File tree

1 file changed

+47
-5
lines changed

1 file changed

+47
-5
lines changed

src/bin/pg_dump/pg_dumpall.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,7 +2116,7 @@ doConnStrQuoting(PQExpBuffer buf, const char *str)
21162116

21172117
/*
21182118
* Append the given string to the shell command being built in the buffer,
2119-
* with suitable shell-style quoting.
2119+
* with suitable shell-style quoting to create exactly one argument.
21202120
*
21212121
* Forbid LF or CR characters, which have scant practical use beyond designing
21222122
* security breaches. The Windows command shell is unusable as a conduit for
@@ -2148,8 +2148,20 @@ doShellQuoting(PQExpBuffer buf, const char *str)
21482148
}
21492149
appendPQExpBufferChar(buf, '\'');
21502150
#else /* WIN32 */
2151+
int backslash_run_length = 0;
21512152

2152-
appendPQExpBufferChar(buf, '"');
2153+
/*
2154+
* A Windows system() argument experiences two layers of interpretation.
2155+
* First, cmd.exe interprets the string. Its behavior is undocumented,
2156+
* but a caret escapes any byte except LF or CR that would otherwise have
2157+
* special meaning. Handling of a caret before LF or CR differs between
2158+
* "cmd.exe /c" and other modes, and it is unusable here.
2159+
*
2160+
* Second, the new process parses its command line to construct argv (see
2161+
* https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
2162+
* backslash-double quote sequences specially.
2163+
*/
2164+
appendPQExpBufferStr(buf, "^\"");
21532165
for (p = str; *p; p++)
21542166
{
21552167
if (*p == '\n' || *p == '\r')
@@ -2160,11 +2172,41 @@ doShellQuoting(PQExpBuffer buf, const char *str)
21602172
exit(EXIT_FAILURE);
21612173
}
21622174

2175+
/* Change N backslashes before a double quote to 2N+1 backslashes. */
21632176
if (*p == '"')
2164-
appendPQExpBuffer(buf, "\\\"");
2177+
{
2178+
while (backslash_run_length)
2179+
{
2180+
appendPQExpBufferStr(buf, "^\\");
2181+
backslash_run_length--;
2182+
}
2183+
appendPQExpBufferStr(buf, "^\\");
2184+
}
2185+
else if (*p == '\\')
2186+
backslash_run_length++;
21652187
else
2166-
appendPQExpBufferChar(buf, *p);
2188+
backslash_run_length = 0;
2189+
2190+
/*
2191+
* Decline to caret-escape the most mundane characters, to ease
2192+
* debugging and lest we approach the command length limit.
2193+
*/
2194+
if (!((*p >= 'a' && *p <= 'z') ||
2195+
(*p >= 'A' && *p <= 'Z') ||
2196+
(*p >= '0' && *p <= '9')))
2197+
appendPQExpBufferChar(buf, '^');
2198+
appendPQExpBufferChar(buf, *p);
2199+
}
2200+
2201+
/*
2202+
* Change N backslashes at end of argument to 2N backslashes, because they
2203+
* precede the double quote that terminates the argument.
2204+
*/
2205+
while (backslash_run_length)
2206+
{
2207+
appendPQExpBufferStr(buf, "^\\");
2208+
backslash_run_length--;
21672209
}
2168-
appendPQExpBufferChar(buf, '"');
2210+
appendPQExpBufferStr(buf, "^\"");
21692211
#endif /* WIN32 */
21702212
}

0 commit comments

Comments
 (0)