summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam A. Rowe Jr <wrowe@apache.org>2019-03-20 23:03:02 +0000
committerWilliam A. Rowe Jr <wrowe@apache.org>2019-03-20 23:03:02 +0000
commita05bc5213c1ac82e85c0167d158548436bd5a6c9 (patch)
treead6bfe9bd54283f12cc01a2addb570006a88cee6
parent06e0fdce4625ff5d56e0f7123a5d84d898059ddd (diff)
downloadapr-a05bc5213c1ac82e85c0167d158548436bd5a6c9.tar.gz
Narrow symbolic link detection on NTFS
Read the WIN32_FIND_DATA::dwReserved0 field to determine whether reparse point is a "name surrogate". It's probably more safe to bind to specifig tags. If provided structure (wininfo) was not resulted from FindFile* call, then additional FindFirstFile call is performed. However this may be unnecessary, because the alternate GetFileInformation call is used in the case of an open file handle, and APR_FINFO_LINK has no meaning when it comes to open files. Submitted by: Oleg Liatte <olegliatte gmail.com> git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1855949 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES4
-rw-r--r--file_io/win32/dir.c2
-rw-r--r--file_io/win32/filestat.c103
-rw-r--r--include/arch/win32/apr_arch_file_io.h3
4 files changed, 97 insertions, 15 deletions
diff --git a/CHANGES b/CHANGES
index ef5817f3d..18dc42dec 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
-*- coding: utf-8 -*-
Changes for APR 2.0.0
+ *) apr_file_info: [Win32 only] Treat only "name surrogate" reparse points
+ as symlinks, and not other reparse tag types. PR47630
+ [Oleg Liatte <olegliatte gmail.com>]
+
*) Test %ld vs. %lld to avoid compiler emits using APR_OFF_T_FMT, in the
case of apparently equivilant long and long long types. [William Rowe]
diff --git a/file_io/win32/dir.c b/file_io/win32/dir.c
index 5c30af8fd..8c8b74546 100644
--- a/file_io/win32/dir.c
+++ b/file_io/win32/dir.c
@@ -210,7 +210,7 @@ APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
#endif
fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) thedir->w.entry,
- 0, wanted);
+ 0, 1, fname, wanted);
finfo->pool = thedir->pool;
finfo->valid |= APR_FINFO_NAME;
diff --git a/file_io/win32/filestat.c b/file_io/win32/filestat.c
index ba4160785..904124db7 100644
--- a/file_io/win32/filestat.c
+++ b/file_io/win32/filestat.c
@@ -210,6 +210,71 @@ static apr_status_t guess_protection_bits(apr_finfo_t *finfo,
return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
}
+static int reparse_point_is_link(WIN32_FILE_ATTRIBUTE_DATA *wininfo,
+ int finddata, const char *fname)
+{
+ int tag = 0;
+
+ if (!(wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ return 0;
+ }
+
+ if (finddata)
+ {
+ // no matter A or W as we don't need file name
+ tag = ((WIN32_FIND_DATAA*)wininfo)->dwReserved0;
+ }
+ else
+ {
+ if (test_safe_name(fname) != APR_SUCCESS) {
+ return 0;
+ }
+
+#if APR_HAS_UNICODE_FS
+ IF_WIN_OS_IS_UNICODE
+ {
+ apr_wchar_t wfname[APR_PATH_MAX];
+ HANDLE hFind;
+ WIN32_FIND_DATAW fd;
+
+ if (utf8_to_unicode_path(wfname, APR_PATH_MAX, fname) != APR_SUCCESS) {
+ return 0;
+ }
+
+ hFind = FindFirstFileW(wfname, &fd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ FindClose(hFind);
+
+ tag = fd.dwReserved0;
+ }
+#endif
+#if APR_HAS_ANSI_FS || 1
+ ELSE_WIN_OS_IS_ANSI
+ {
+ HANDLE hFind;
+ WIN32_FIND_DATAA fd;
+
+ hFind = FindFirstFileA(fname, &fd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ FindClose(hFind);
+
+ tag = fd.dwReserved0;
+ }
+#endif
+ }
+
+ // Test "Name surrogate bit" to detect any kind of symbolic link
+ // See https://docs.microsoft.com/en-us/windows/desktop/fileio/reparse-point-tags
+ return tag & 0x20000000;
+}
+
apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile,
apr_int32_t wanted, int whatfile)
{
@@ -351,7 +416,10 @@ apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile,
*/
int fillin_fileinfo(apr_finfo_t *finfo,
WIN32_FILE_ATTRIBUTE_DATA *wininfo,
- int byhandle, apr_int32_t wanted)
+ int byhandle,
+ int finddata,
+ const char *fname,
+ apr_int32_t wanted)
{
DWORD *sizes = &wininfo->nFileSizeHigh + byhandle;
int warn = 0;
@@ -372,7 +440,7 @@ int fillin_fileinfo(apr_finfo_t *finfo,
#endif
if (wanted & APR_FINFO_LINK &&
- wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ reparse_point_is_link(wininfo, finddata, fname)) {
finfo->filetype = APR_LNK;
}
else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
@@ -449,7 +517,7 @@ APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t want
return apr_get_os_error();
}
- fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, wanted);
+ fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, 0, thefile->fname, wanted);
if (finfo->filetype == APR_REG)
{
@@ -520,6 +588,7 @@ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
WIN32_FIND_DATAA n;
WIN32_FILE_ATTRIBUTE_DATA i;
} FileInfo;
+ int finddata = 0;
/* Catch fname length == MAX_PATH since GetFileAttributesEx fails
* with PATH_NOT_FOUND. We would rather indicate length error than
@@ -555,7 +624,7 @@ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname)
/ sizeof(apr_wchar_t), fname)))
return rv;
- if (!(wanted & APR_FINFO_NAME)) {
+ if (!(wanted & (APR_FINFO_NAME | APR_FINFO_LINK))) {
if (!GetFileAttributesExW(wfname, GetFileExInfoStandard,
&FileInfo.i))
return apr_get_os_error();
@@ -565,7 +634,6 @@ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
* since we want the true name, and set aside a long
* enough string to handle the longest file name.
*/
- char tmpname[APR_FILE_MAX * 3 + 1];
HANDLE hFind;
if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
return rv;
@@ -574,11 +642,17 @@ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
if (hFind == INVALID_HANDLE_VALUE)
return apr_get_os_error();
FindClose(hFind);
- if (unicode_to_utf8_path(tmpname, sizeof(tmpname),
- FileInfo.w.cFileName)) {
- return APR_ENAMETOOLONG;
+ finddata = 1;
+
+ if (wanted & APR_FINFO_NAME)
+ {
+ char tmpname[APR_FILE_MAX * 3 + 1];
+ if (unicode_to_utf8_path(tmpname, sizeof(tmpname),
+ FileInfo.w.cFileName)) {
+ return APR_ENAMETOOLONG;
+ }
+ filename = apr_pstrdup(pool, tmpname);
}
- filename = apr_pstrdup(pool, tmpname);
}
}
#endif
@@ -590,7 +664,7 @@ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
rv = apr_filepath_root(&root, &test, APR_FILEPATH_NATIVE, pool);
isroot = (root && *root && !(*test));
- if ((apr_os_level >= APR_WIN_98) && (!(wanted & APR_FINFO_NAME) || isroot))
+ if ((apr_os_level >= APR_WIN_98) && (!(wanted & (APR_FINFO_NAME | APR_FINFO_LINK)) || isroot))
{
/* cannot use FindFile on a Win98 root, it returns \*
* GetFileAttributesExA is not available on Win95
@@ -632,16 +706,19 @@ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
hFind = FindFirstFileA(fname, &FileInfo.n);
if (hFind == INVALID_HANDLE_VALUE) {
return apr_get_os_error();
- }
+ }
FindClose(hFind);
- filename = apr_pstrdup(pool, FileInfo.n.cFileName);
+ finddata = 1;
+ if (wanted & APR_FINFO_NAME) {
+ filename = apr_pstrdup(pool, FileInfo.n.cFileName);
+ }
}
}
#endif
if (ident_rv != APR_INCOMPLETE) {
if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo,
- 0, wanted))
+ 0, finddata, fname, wanted))
{
/* Go the extra mile to assure we have a file. WinNT/2000 seems
* to reliably translate char devices to the path '\\.\device'
diff --git a/include/arch/win32/apr_arch_file_io.h b/include/arch/win32/apr_arch_file_io.h
index 7c5996cab..5302a15be 100644
--- a/include/arch/win32/apr_arch_file_io.h
+++ b/include/arch/win32/apr_arch_file_io.h
@@ -135,7 +135,8 @@ void *res_name_from_filename(const char *file, int global, apr_pool_t *pool);
/* Private function for apr_stat/lstat/getfileinfo/dir_read */
int fillin_fileinfo(apr_finfo_t *finfo, WIN32_FILE_ATTRIBUTE_DATA *wininfo,
- int byhandle, apr_int32_t wanted);
+ int byhandle, int finddata, const char *fname,
+ apr_int32_t wanted);
/* Private function that extends apr_stat/lstat/getfileinfo/dir_read */
apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile,