Skip to content

Commit 41f18f0

Browse files
committed
Promote pg_dumpall shell/connstr quoting functions to src/fe_utils.
Rename these newly-extern functions with terms more typical of their new neighbors. No functional changes; a subsequent commit will use them in more places. Back-patch to 9.1 (all supported versions). Back branches lack src/fe_utils, so instead rename the functions in place; the subsequent commit will copy them into the other programs using them. Security: CVE-2016-5424
1 parent bd65371 commit 41f18f0

File tree

4 files changed

+157
-154
lines changed

4 files changed

+157
-154
lines changed

src/bin/pg_dump/pg_dumpall.c

Lines changed: 7 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ static void makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
5050
const char *name2);
5151
static void dumpDatabases(PGconn *conn);
5252
static void dumpTimestamp(const char *msg);
53-
static void doShellQuoting(PQExpBuffer buf, const char *str);
54-
static void doConnStrQuoting(PQExpBuffer buf, const char *str);
5553

5654
static int runPgDump(const char *dbname);
5755
static void buildShSecLabels(PGconn *conn, const char *catalog_name,
@@ -215,7 +213,7 @@ main(int argc, char *argv[])
215213
case 'f':
216214
filename = pg_strdup(optarg);
217215
appendPQExpBufferStr(pgdumpopts, " -f ");
218-
doShellQuoting(pgdumpopts, filename);
216+
appendShellString(pgdumpopts, filename);
219217
break;
220218

221219
case 'g':
@@ -252,7 +250,7 @@ main(int argc, char *argv[])
252250

253251
case 'S':
254252
appendPQExpBufferStr(pgdumpopts, " -S ");
255-
doShellQuoting(pgdumpopts, optarg);
253+
appendShellString(pgdumpopts, optarg);
256254
break;
257255

258256
case 't':
@@ -288,13 +286,13 @@ main(int argc, char *argv[])
288286

289287
case 2:
290288
appendPQExpBufferStr(pgdumpopts, " --lock-wait-timeout ");
291-
doShellQuoting(pgdumpopts, optarg);
289+
appendShellString(pgdumpopts, optarg);
292290
break;
293291

294292
case 3:
295293
use_role = pg_strdup(optarg);
296294
appendPQExpBufferStr(pgdumpopts, " --role ");
297-
doShellQuoting(pgdumpopts, use_role);
295+
appendShellString(pgdumpopts, use_role);
298296
break;
299297

300298
default:
@@ -1814,9 +1812,9 @@ runPgDump(const char *dbname)
18141812
* string.
18151813
*/
18161814
appendPQExpBuffer(connstrbuf, "%s dbname=", connstr);
1817-
doConnStrQuoting(connstrbuf, dbname);
1815+
appendConnStrVal(connstrbuf, dbname);
18181816

1819-
doShellQuoting(cmd, connstrbuf->data);
1817+
appendShellString(cmd, connstrbuf->data);
18201818

18211819
if (verbose)
18221820
fprintf(stderr, _("%s: running \"%s\"\n"), progname, cmd->data);
@@ -2096,7 +2094,7 @@ constructConnStr(const char **keywords, const char **values)
20962094
appendPQExpBufferChar(buf, ' ');
20972095
firstkeyword = false;
20982096
appendPQExpBuffer(buf, "%s=", keywords[i]);
2099-
doConnStrQuoting(buf, values[i]);
2097+
appendConnStrVal(buf, values[i]);
21002098
}
21012099

21022100
connstr = pg_strdup(buf->data);
@@ -2169,145 +2167,3 @@ dumpTimestamp(const char *msg)
21692167
if (strftime(buf, sizeof(buf), PGDUMP_STRFTIME_FMT, localtime(&now)) != 0)
21702168
fprintf(OPF, "-- %s %s\n\n", msg, buf);
21712169
}
2172-
2173-
2174-
/*
2175-
* Append the given string to the buffer, with suitable quoting for passing
2176-
* the string as a value, in a keyword/pair value in a libpq connection
2177-
* string
2178-
*/
2179-
static void
2180-
doConnStrQuoting(PQExpBuffer buf, const char *str)
2181-
{
2182-
const char *s;
2183-
bool needquotes;
2184-
2185-
/*
2186-
* If the string consists entirely of plain ASCII characters, no need to
2187-
* quote it. This is quite conservative, but better safe than sorry.
2188-
*/
2189-
needquotes = false;
2190-
for (s = str; *s; s++)
2191-
{
2192-
if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
2193-
(*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
2194-
{
2195-
needquotes = true;
2196-
break;
2197-
}
2198-
}
2199-
2200-
if (needquotes)
2201-
{
2202-
appendPQExpBufferChar(buf, '\'');
2203-
while (*str)
2204-
{
2205-
/* ' and \ must be escaped by to \' and \\ */
2206-
if (*str == '\'' || *str == '\\')
2207-
appendPQExpBufferChar(buf, '\\');
2208-
2209-
appendPQExpBufferChar(buf, *str);
2210-
str++;
2211-
}
2212-
appendPQExpBufferChar(buf, '\'');
2213-
}
2214-
else
2215-
appendPQExpBufferStr(buf, str);
2216-
}
2217-
2218-
/*
2219-
* Append the given string to the shell command being built in the buffer,
2220-
* with suitable shell-style quoting to create exactly one argument.
2221-
*
2222-
* Forbid LF or CR characters, which have scant practical use beyond designing
2223-
* security breaches. The Windows command shell is unusable as a conduit for
2224-
* arguments containing LF or CR characters. A future major release should
2225-
* reject those characters in CREATE ROLE and CREATE DATABASE, because use
2226-
* there eventually leads to errors here.
2227-
*/
2228-
static void
2229-
doShellQuoting(PQExpBuffer buf, const char *str)
2230-
{
2231-
const char *p;
2232-
2233-
#ifndef WIN32
2234-
appendPQExpBufferChar(buf, '\'');
2235-
for (p = str; *p; p++)
2236-
{
2237-
if (*p == '\n' || *p == '\r')
2238-
{
2239-
fprintf(stderr,
2240-
_("shell command argument contains a newline or carriage return: \"%s\"\n"),
2241-
str);
2242-
exit(EXIT_FAILURE);
2243-
}
2244-
2245-
if (*p == '\'')
2246-
appendPQExpBufferStr(buf, "'\"'\"'");
2247-
else
2248-
appendPQExpBufferChar(buf, *p);
2249-
}
2250-
appendPQExpBufferChar(buf, '\'');
2251-
#else /* WIN32 */
2252-
int backslash_run_length = 0;
2253-
2254-
/*
2255-
* A Windows system() argument experiences two layers of interpretation.
2256-
* First, cmd.exe interprets the string. Its behavior is undocumented,
2257-
* but a caret escapes any byte except LF or CR that would otherwise have
2258-
* special meaning. Handling of a caret before LF or CR differs between
2259-
* "cmd.exe /c" and other modes, and it is unusable here.
2260-
*
2261-
* Second, the new process parses its command line to construct argv (see
2262-
* https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
2263-
* backslash-double quote sequences specially.
2264-
*/
2265-
appendPQExpBufferStr(buf, "^\"");
2266-
for (p = str; *p; p++)
2267-
{
2268-
if (*p == '\n' || *p == '\r')
2269-
{
2270-
fprintf(stderr,
2271-
_("shell command argument contains a newline or carriage return: \"%s\"\n"),
2272-
str);
2273-
exit(EXIT_FAILURE);
2274-
}
2275-
2276-
/* Change N backslashes before a double quote to 2N+1 backslashes. */
2277-
if (*p == '"')
2278-
{
2279-
while (backslash_run_length)
2280-
{
2281-
appendPQExpBufferStr(buf, "^\\");
2282-
backslash_run_length--;
2283-
}
2284-
appendPQExpBufferStr(buf, "^\\");
2285-
}
2286-
else if (*p == '\\')
2287-
backslash_run_length++;
2288-
else
2289-
backslash_run_length = 0;
2290-
2291-
/*
2292-
* Decline to caret-escape the most mundane characters, to ease
2293-
* debugging and lest we approach the command length limit.
2294-
*/
2295-
if (!((*p >= 'a' && *p <= 'z') ||
2296-
(*p >= 'A' && *p <= 'Z') ||
2297-
(*p >= '0' && *p <= '9')))
2298-
appendPQExpBufferChar(buf, '^');
2299-
appendPQExpBufferChar(buf, *p);
2300-
}
2301-
2302-
/*
2303-
* Change N backslashes at end of argument to 2N backslashes, because they
2304-
* precede the double quote that terminates the argument.
2305-
*/
2306-
while (backslash_run_length)
2307-
{
2308-
appendPQExpBufferStr(buf, "^\\");
2309-
backslash_run_length--;
2310-
}
2311-
appendPQExpBufferStr(buf, "^\"");
2312-
#endif /* WIN32 */
2313-
}

src/fe_utils/string_utils.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,149 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
378378
}
379379

380380

381+
/*
382+
* Append the given string to the shell command being built in the buffer,
383+
* with suitable shell-style quoting to create exactly one argument.
384+
*
385+
* Forbid LF or CR characters, which have scant practical use beyond designing
386+
* security breaches. The Windows command shell is unusable as a conduit for
387+
* arguments containing LF or CR characters. A future major release should
388+
* reject those characters in CREATE ROLE and CREATE DATABASE, because use
389+
* there eventually leads to errors here.
390+
*/
391+
void
392+
appendShellString(PQExpBuffer buf, const char *str)
393+
{
394+
const char *p;
395+
396+
#ifndef WIN32
397+
appendPQExpBufferChar(buf, '\'');
398+
for (p = str; *p; p++)
399+
{
400+
if (*p == '\n' || *p == '\r')
401+
{
402+
fprintf(stderr,
403+
_("shell command argument contains a newline or carriage return: \"%s\"\n"),
404+
str);
405+
exit(EXIT_FAILURE);
406+
}
407+
408+
if (*p == '\'')
409+
appendPQExpBufferStr(buf, "'\"'\"'");
410+
else
411+
appendPQExpBufferChar(buf, *p);
412+
}
413+
appendPQExpBufferChar(buf, '\'');
414+
#else /* WIN32 */
415+
int backslash_run_length = 0;
416+
417+
/*
418+
* A Windows system() argument experiences two layers of interpretation.
419+
* First, cmd.exe interprets the string. Its behavior is undocumented,
420+
* but a caret escapes any byte except LF or CR that would otherwise have
421+
* special meaning. Handling of a caret before LF or CR differs between
422+
* "cmd.exe /c" and other modes, and it is unusable here.
423+
*
424+
* Second, the new process parses its command line to construct argv (see
425+
* https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
426+
* backslash-double quote sequences specially.
427+
*/
428+
appendPQExpBufferStr(buf, "^\"");
429+
for (p = str; *p; p++)
430+
{
431+
if (*p == '\n' || *p == '\r')
432+
{
433+
fprintf(stderr,
434+
_("shell command argument contains a newline or carriage return: \"%s\"\n"),
435+
str);
436+
exit(EXIT_FAILURE);
437+
}
438+
439+
/* Change N backslashes before a double quote to 2N+1 backslashes. */
440+
if (*p == '"')
441+
{
442+
while (backslash_run_length)
443+
{
444+
appendPQExpBufferStr(buf, "^\\");
445+
backslash_run_length--;
446+
}
447+
appendPQExpBufferStr(buf, "^\\");
448+
}
449+
else if (*p == '\\')
450+
backslash_run_length++;
451+
else
452+
backslash_run_length = 0;
453+
454+
/*
455+
* Decline to caret-escape the most mundane characters, to ease
456+
* debugging and lest we approach the command length limit.
457+
*/
458+
if (!((*p >= 'a' && *p <= 'z') ||
459+
(*p >= 'A' && *p <= 'Z') ||
460+
(*p >= '0' && *p <= '9')))
461+
appendPQExpBufferChar(buf, '^');
462+
appendPQExpBufferChar(buf, *p);
463+
}
464+
465+
/*
466+
* Change N backslashes at end of argument to 2N backslashes, because they
467+
* precede the double quote that terminates the argument.
468+
*/
469+
while (backslash_run_length)
470+
{
471+
appendPQExpBufferStr(buf, "^\\");
472+
backslash_run_length--;
473+
}
474+
appendPQExpBufferStr(buf, "^\"");
475+
#endif /* WIN32 */
476+
}
477+
478+
479+
/*
480+
* Append the given string to the buffer, with suitable quoting for passing
481+
* the string as a value, in a keyword/pair value in a libpq connection
482+
* string
483+
*/
484+
void
485+
appendConnStrVal(PQExpBuffer buf, const char *str)
486+
{
487+
const char *s;
488+
bool needquotes;
489+
490+
/*
491+
* If the string consists entirely of plain ASCII characters, no need to
492+
* quote it. This is quite conservative, but better safe than sorry.
493+
*/
494+
needquotes = false;
495+
for (s = str; *s; s++)
496+
{
497+
if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
498+
(*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
499+
{
500+
needquotes = true;
501+
break;
502+
}
503+
}
504+
505+
if (needquotes)
506+
{
507+
appendPQExpBufferChar(buf, '\'');
508+
while (*str)
509+
{
510+
/* ' and \ must be escaped by to \' and \\ */
511+
if (*str == '\'' || *str == '\\')
512+
appendPQExpBufferChar(buf, '\\');
513+
514+
appendPQExpBufferChar(buf, *str);
515+
str++;
516+
}
517+
appendPQExpBufferChar(buf, '\'');
518+
}
519+
else
520+
appendPQExpBufferStr(buf, str);
521+
}
522+
523+
381524
/*
382525
* Deconstruct the text representation of a 1-dimensional Postgres array
383526
* into individual items.

src/include/fe_utils/string_utils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
*
33
* String-processing utility routines for frontend code
44
*
5-
* Assorted utility functions that are useful in constructing SQL queries
6-
* and interpreting backend output.
5+
* Utility functions that interpret backend output or quote strings for
6+
* assorted contexts.
77
*
88
*
99
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
@@ -40,6 +40,9 @@ extern void appendByteaLiteral(PQExpBuffer buf,
4040
const unsigned char *str, size_t length,
4141
bool std_strings);
4242

43+
extern void appendShellString(PQExpBuffer buf, const char *str);
44+
extern void appendConnStrVal(PQExpBuffer buf, const char *str);
45+
4346
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
4447

4548
extern bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,

src/port/system.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
* Win32 needs double quotes at the beginning and end of system()
88
* strings. If not, it gets confused with multiple quoted strings.
99
* It also requires double-quotes around the executable name and
10-
* any files used for redirection. Other args can use single-quotes.
10+
* any files used for redirection. Filter other args through
11+
* appendShellString() to quote them.
1112
*
1213
* Generated using Win32 "CMD /?":
1314
*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy