Skip to content

Commit 0de0cc1

Browse files
committed
Properly handle Win32 paths of 'E:abc', which can be either absolute or
relative, by creating a function path_is_relative_and_below_cwd() to check for specific requirements. It is unclear if this fixes a security problem or not but the new code is more robust.
1 parent b313bca commit 0de0cc1

File tree

4 files changed

+73
-48
lines changed

4 files changed

+73
-48
lines changed

contrib/adminpack/adminpack.c

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,32 +73,30 @@ convert_and_check_filename(text *arg, bool logAllowed)
7373

7474
canonicalize_path(filename); /* filename can change length here */
7575

76-
/* Disallow ".." in the path */
77-
if (path_contains_parent_reference(filename))
78-
ereport(ERROR,
79-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
80-
(errmsg("reference to parent directory (\"..\") not allowed"))));
81-
8276
if (is_absolute_path(filename))
8377
{
84-
/* Allow absolute references within DataDir */
85-
if (path_is_prefix_of_path(DataDir, filename))
86-
return filename;
87-
/* The log directory might be outside our datadir, but allow it */
88-
if (logAllowed &&
89-
is_absolute_path(Log_directory) &&
90-
path_is_prefix_of_path(Log_directory, filename))
91-
return filename;
92-
93-
ereport(ERROR,
78+
/* Disallow '/a/b/data/..' */
79+
if (path_contains_parent_reference(filename))
80+
ereport(ERROR,
81+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
82+
(errmsg("reference to parent directory (\"..\") not allowed"))));
83+
/*
84+
* Allow absolute paths if within DataDir or Log_directory, even
85+
* though Log_directory might be outside DataDir.
86+
*/
87+
if (!path_is_prefix_of_path(DataDir, filename) &&
88+
(!logAllowed || !is_absolute_path(Log_directory) ||
89+
!path_is_prefix_of_path(Log_directory, filename)))
90+
ereport(ERROR,
9491
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9592
(errmsg("absolute path not allowed"))));
96-
return NULL; /* keep compiler quiet */
97-
}
98-
else
99-
{
100-
return filename;
10193
}
94+
else if (!path_is_relative_and_below_cwd(filename))
95+
ereport(ERROR,
96+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
97+
(errmsg("path must be in or below the current directory"))));
98+
99+
return filename;
102100
}
103101

104102

src/backend/utils/adt/genfile.c

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,31 +51,30 @@ convert_and_check_filename(text *arg)
5151
filename = text_to_cstring(arg);
5252
canonicalize_path(filename); /* filename can change length here */
5353

54-
/* Disallow ".." in the path */
55-
if (path_contains_parent_reference(filename))
56-
ereport(ERROR,
57-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
58-
(errmsg("reference to parent directory (\"..\") not allowed"))));
59-
6054
if (is_absolute_path(filename))
6155
{
62-
/* Allow absolute references within DataDir */
63-
if (path_is_prefix_of_path(DataDir, filename))
64-
return filename;
65-
/* The log directory might be outside our datadir, but allow it */
66-
if (is_absolute_path(Log_directory) &&
67-
path_is_prefix_of_path(Log_directory, filename))
68-
return filename;
69-
70-
ereport(ERROR,
56+
/* Disallow '/a/b/data/..' */
57+
if (path_contains_parent_reference(filename))
58+
ereport(ERROR,
59+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
60+
(errmsg("reference to parent directory (\"..\") not allowed"))));
61+
/*
62+
* Allow absolute paths if within DataDir or Log_directory, even
63+
* though Log_directory might be outside DataDir.
64+
*/
65+
if (!path_is_prefix_of_path(DataDir, filename) &&
66+
(!is_absolute_path(Log_directory) ||
67+
!path_is_prefix_of_path(Log_directory, filename)))
68+
ereport(ERROR,
7169
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7270
(errmsg("absolute path not allowed"))));
73-
return NULL; /* keep compiler quiet */
74-
}
75-
else
76-
{
77-
return filename;
7871
}
72+
else if (!path_is_relative_and_below_cwd(filename))
73+
ereport(ERROR,
74+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
75+
(errmsg("path must be in or below the current directory"))));
76+
77+
return filename;
7978
}
8079

8180

src/include/port.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ extern void join_path_components(char *ret_path,
4242
extern void canonicalize_path(char *path);
4343
extern void make_native_path(char *path);
4444
extern bool path_contains_parent_reference(const char *path);
45+
extern bool path_is_relative_and_below_cwd(const char *path);
4546
extern bool path_is_prefix_of_path(const char *path1, const char *path2);
4647
extern const char *get_progname(const char *argv0);
4748
extern void get_share_path(const char *my_exec_path, char *ret_path);
@@ -77,13 +78,7 @@ extern void pgfnames_cleanup(char **filenames);
7778
#else
7879
#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\')
7980

80-
/*
81-
* On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
82-
* relative to the cwd on that drive, or the drive's root directory
83-
* if that drive has no cwd. Because the path itself cannot tell us
84-
* which is the case, we have to assume the worst, i.e. that it is not
85-
* absolute; this check is done by IS_DIR_SEP(filename[2]).
86-
*/
81+
/* See path_is_relative_and_below_cwd() for how we handle 'E:abc'. */
8782
#define is_absolute_path(filename) \
8883
( \
8984
IS_DIR_SEP((filename)[0]) || \

src/port/path.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,39 @@ path_contains_parent_reference(const char *path)
358358
return false;
359359
}
360360

361+
/*
362+
* Detect whether a path is only in or below the current working directory.
363+
* An absolute path that matches the current working directory should
364+
* return false (we only want relative to the cwd). We don't allow
365+
* "/../" even if that would keep us under the cwd (it is too hard to
366+
* track that).
367+
*/
368+
bool
369+
path_is_relative_and_below_cwd(const char *path)
370+
{
371+
if (!is_absolute_path(path))
372+
return false;
373+
/* don't allow anything above the cwd */
374+
else if (path_contains_parent_reference(path))
375+
return false;
376+
#ifdef WIN32
377+
/*
378+
* On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
379+
* relative to the cwd on that drive, or the drive's root directory
380+
* if that drive has no cwd. Because the path itself cannot tell us
381+
* which is the case, we have to assume the worst, i.e. that it is not
382+
* below the cwd. We could use GetFullPathName() to find the full path
383+
* but that could change if the current directory for the drive changes
384+
* underneath us, so we just disallow it.
385+
*/
386+
else if (isalpha((unsigned char) path[0]) && path[1] == ':' &&
387+
!IS_DIR_SEP(path[2]))
388+
return false;
389+
#endif
390+
else
391+
return true;
392+
}
393+
361394
/*
362395
* Detect whether path1 is a prefix of path2 (including equality).
363396
*

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