8000 Fix Windows shell argument quoting. · s-monk/postgres@18392ed · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 18392ed

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 6bec1a6 commit 18392ed

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
@@ -2128,7 +2128,7 @@ doConnStrQuoting(PQExpBuffer buf, const char *str)
21282128

21292129
/*
21302130
* Append the given string to the shell command being built in the buffer,
2131-
* with suitable shell-style quoting.
2131+
* with suitable shell-style quoting to create exactly one argument.
21322132
*
21332133
* Forbid LF or CR characters, which have scant practical use beyond designing
21342134
* security breaches. The Windows command shell is unusable as a conduit for
@@ -2160,8 +2160,20 @@ doShellQuoting(PQExpBuffer buf, const char *str)
21602160
}
21612161
appendPQExpBufferChar(buf, '\'');
21622162
#else /* WIN32 */
2163+
int backslash_run_length = 0;
21632164

2164-
appendPQExpBufferChar(buf, '"');
2165+
/*
2166+
* A Windows system() argument experiences two layers of interpretation.
2167+
* First, cmd.exe interprets the string. Its behavior is undocumented,
2168+
* but a caret escapes any byte except LF or CR that would otherwise have
2169+
* special meaning. Handling of a caret before LF or CR differs between
2170+
* "cmd.exe /c" and other modes, and it is unusable here.
2171+
*
2172+
* Second, the new process parses its command line to construct argv (see
2173+
* https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
2174+
* backslash-double quote sequences specially.
2175+
*/
2176+
appendPQExpBufferStr(buf, "^\"");
21652177
for (p = str; *p; p++)
21662178
{
21672179
if (*p == '\n' || *p == '\r')
@@ -2172,11 +2184,41 @@ doShellQuoting(PQExpBuffer buf, const char *str)
21722184
exit(EXIT_FAILURE);
21732185
}
21742186

2187+
/* Change N backslashes before a double quote to 2N+1 backslashes. */
21752188
if (*p == '"')
2176-
appendPQExpBufferStr(buf, "\\\"");
2189+
{
2190+
while (backslash_run_length)
2191+
{
2192+
appendPQExpBufferStr(buf, "^\\");
2193+
backslash_run_length--;
2194+
}
2195+
appendPQExpBufferStr(buf, "^\\");
2196+
}
2197+
else if (*p == '\\')
2198+
backslash_run_length++;
21772199
else
2178-
appendPQExpBufferChar(buf, *p);
2200+
backslash_run_length = 0;
2201+
2202+
/*
2203+
* Decline to caret-escape the most mundane characters, to ease
2204+
* debugging and lest we approach the command length limit.
2205+
*/
2206+
if (!((*p >= 'a' && *p <= 'z') ||
2207+
(*p >= 'A' && *p <= 'Z') ||
2208+
(*p >= '0' && *p <= '9')))
2209+
appendPQExpBufferChar(buf, '^');
2210+
appendPQExpBufferChar(buf, *p);
2211+
}
2212+
2213+
/*
2214+
* Change N backslashes at end of argument to 2N backslashes, because they
2215+
* precede the double quote that terminates the argument.
2216+
*/
2217+
while (backslash_run_length)
2218+
{
2219+
appendPQExpBufferStr(buf, "^\\");
2220+
backslash_run_length--;
21792221
}
2180-
appendPQExpBufferChar(buf, '"');
2222+
appendPQExpBufferStr(buf, "^\"");
21812223
#endif /* WIN32 */
21822224
}

0 commit comments

Comments
 (0)
0