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

Skip to content

Commit 2d69f5b

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 61c2cd8 commit 2d69f5b

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

19051905
/*
19061906
* Append the given string to the shell command being built in the buffer,
1907-
* with suitable shell-style quoting.
1907+
* with suitable shell-style quoting to create exactly one argument.
19081908
*
19091909
* Forbid LF or CR characters, which have scant practical use beyond designing
19101910
* security breaches. The Windows command shell is unusable as a conduit for
@@ -1936,8 +1936,20 @@ doShellQuoting(PQExpBuffer buf, const char *str)
19361936
}
19371937
appendPQExpBufferChar(buf, '\'');
19381938
#else /* WIN32 */
1939+
int backslash_run_length = 0;
19391940

1940-
appendPQExpBufferChar(buf, '"');
1941+
/*
1942+
* A Windows system() argument experiences two layers of interpretation.
1943+
* First, cmd.exe interprets the string. Its behavior is undocumented,
1944+
* but a caret escapes any byte except LF or CR that would otherwise have
1945+
* special meaning. Handling of a caret before LF or CR differs between
1946+
* "cmd.exe /c" and other modes, and it is unusable here.
1947+
*
1948+
* Second, the new process parses its command line to construct argv (see
1949+
* https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
1950+
* backslash-double quote sequences specially.
1951+
*/
1952+
appendPQExpBufferStr(buf, "^\"");
19411953
for (p = str; *p; p++)
19421954
{
19431955
if (*p == '\n' || *p == '\r')
@@ -1948,11 +1960,41 @@ doShellQuoting(PQExpBuffer buf, const char *str)
19481960
exit(EXIT_FAILURE);
19491961
}
19501962

1963+
/* Change N backslashes before a double quote to 2N+1 backslashes. */
19511964
if (*p == '"')
1952-
appendPQExpBuffer(buf, "\\\"");
1965+
{
< 104B6 /code>1966+
while (backslash_run_length)
1967+
{
1968+
appendPQExpBufferStr(buf, "^\\");
1969+
backslash_run_length--;
1970+
}
1971+
appendPQExpBufferStr(buf, "^\\");
1972+
}
1973+
else if (*p == '\\')
1974+
backslash_run_length++;
19531975
else
1954-
appendPQExpBufferChar(buf, *p);
1976+
backslash_run_length = 0;
1977+
1978+
/*
1979+
* Decline to caret-escape the most mundane characters, to ease
1980+
* debugging and lest we approach the command length limit.
1981+
*/
1982+
if (!((*p >= 'a' && *p <= 'z') ||
1983+
(*p >= 'A' && *p <= 'Z') ||
1984+
(*p >= '0' && *p <= '9')))
1985+
appendPQExpBufferChar(buf, '^');
1986+
appendPQExpBufferChar(buf, *p);
1987+
}
1988+
1989+
/*
1990+
* Change N backslashes at end of argument to 2N backslashes, because they
1991+
* precede the double quote that terminates the argument.
1992+
*/
1993+
while (backslash_run_length)
1994+
{
1995+
appendPQExpBufferStr(buf, "^\\");
1996+
backslash_run_length--;
19551997
}
1956-
appendPQExpBufferChar(buf, '"');
1998+
appendPQExpBufferStr(buf, "^\"");
19571999
#endif /* WIN32 */
19582000
}

0 commit comments

Comments
 (0)
0