Skip to content

Commit 7d69a03

Browse files
committed
win,fs Use AccessCheck() for all access() calls
No longer shy away from using `AccessCheck()` for simple `W_OK`/`R_OK` checks; use it for all calls unconditionally.
1 parent e6f0e49 commit 7d69a03

File tree

1 file changed

+103
-113
lines changed

1 file changed

+103
-113
lines changed

src/win/fs.c

Lines changed: 103 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,133 +2126,123 @@ static void fs__access(uv_fs_t* req) {
21262126
SET_REQ_WIN32_ERROR(req, GetLastError());
21272127
return;
21282128
}
2129+
DWORD sdLen = 0, err = 0, tokenAccess = 0, executeAccessRights = 0,
2130+
grantedAccess = 0, privilegesLen = 0;
2131+
SECURITY_INFORMATION si = NULL;
2132+
PSECURITY_DESCRIPTOR sd = NULL;
2133+
HANDLE hToken = NULL, hImpersonatedToken = NULL;
2134+
GENERIC_MAPPING mapping = { 0xFFFFFFFF };
2135+
PRIVILEGE_SET privileges = { 0 };
2136+
BOOL result = FALSE;
21292137

21302138
/*
2131-
* If write access was requested, ensure that either
2132-
* the requested file is not marked as READONLY,
2133-
* or that it's actually a directory (directories
2134-
* cannot be read-only in Windows)
2135-
*/
2136-
if ((req->fs.info.mode & W_OK) &&
2137-
((attr & FILE_ATTRIBUTE_READONLY) ||
2138-
!(attr & FILE_ATTRIBUTE_DIRECTORY))) {
2139-
SET_REQ_WIN32_ERROR(req, UV_EPERM);
2139+
* First, we must allocate enough space. We do that
2140+
* by first passing in a zero-length null pointer,
2141+
* storing the desired length into `sd_length`.
2142+
* We expect this call to fail with a certain error code.
2143+
*/
2144+
si = OWNER_SECURITY_INFORMATION |
2145+
GROUP_SECURITY_INFORMATION |
2146+
DACL_SECURITY_INFORMATION;
2147+
if (GetFileSecurityW(req->file.pathw, si, NULL, 0, &sdLen)) {
2148+
SET_REQ_RESULT(req, UV_UNKNOWN);
2149+
return;
2150+
}
2151+
err = GetLastError();
2152+
if (ERROR_INSUFFICIENT_BUFFER != err) {
2153+
SET_REQ_WIN32_ERROR(req, err);
21402154
return;
21412155
}
21422156

2143-
/*
2144-
* If executable access was requested, we must check
2145-
* with the AccessCheck() ACL call. This is mildly
2146-
* expensive, so only do it if `X_OK` was requested.
2147-
*/
2148-
if (req->fs.info.mode & X_OK) {
2149-
DWORD sdLen = 0, err = 0, tokenAccess = 0, executeAccessRights = 0,
2150-
grantedAccess = 0, privilegesLen = 0;
2151-
SECURITY_INFORMATION si = NULL;
2152-
PSECURITY_DESCRIPTOR sd = NULL;
2153-
HANDLE hToken = NULL, hImpersonatedToken = NULL;
2154-
GENERIC_MAPPING mapping = { 0xFFFFFFFF };
2155-
PRIVILEGE_SET privileges = { 0 };
2156-
BOOL result = FALSE;
2157-
2158-
/*
2159-
* First, we must allocate enough space. We do that
2160-
* by first passing in a zero-length null pointer,
2161-
* storing the desired length into `sd_length`.
2162-
* We expect this call to fail with a certain error code.
2163-
*/
2164-
si = OWNER_SECURITY_INFORMATION |
2165-
GROUP_SECURITY_INFORMATION |
2166-
DACL_SECURITY_INFORMATION;
2167-
if (GetFileSecurityW(req->file.pathw, si, NULL, 0, &sdLen)) {
2168-
SET_REQ_RESULT(req, UV_UNKNOWN);
2169-
return;
2170-
}
2171-
err = GetLastError();
2172-
if (ERROR_INSUFFICIENT_BUFFER != err) {
2173-
SET_REQ_WIN32_ERROR(req, err);
2174-
return;
2175-
}
2157+
/* Now that we know how big `sd` must be, allocate it */
2158+
sd = (PSECURITY_DESCRIPTOR)uv__malloc(sdLen);
2159+
if (!sd) {
2160+
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
2161+
}
21762162

2177-
/* Now that we know how big `sd` must be, allocate it */
2178-
sd = (PSECURITY_DESCRIPTOR)uv__malloc(sdLen);
2179-
if (!sd) {
2180-
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
2181-
}
2163+
/* Call `GetFileSecurity()` with the requisite `sd` structure. */
2164+
if (!GetFileSecurityW(req->file.pathw, si, sd, sdLen, &sdLen)) {
2165+
SET_REQ_WIN32_ERROR(req, GetLastError());
2166+
goto accesscheck_cleanup;
2167+
}
21822168

2183-
/* Call `GetFileSecurity()` with the requisite `sd` structure. */
2184-
if (!GetFileSecurityW(req->file.pathw, si, sd, sdLen, &sdLen)) {
2185-
SET_REQ_WIN32_ERROR(req, GetLastError());
2186-
goto accesscheck_cleanup;
2187-
}
2169+
/*
2170+
* Next we need to create an impersonation token representing
2171+
* the current user and the current process.
2172+
*/
2173+
tokenAccess = TOKEN_IMPERSONATE |
2174+
TOKEN_QUERY |
2175+
TOKEN_DUPLICATE |
2176+
STANDARD_RIGHTS_READ;
2177+
if (!OpenProcessToken(GetCurrentProcess(), tokenAccess, &hToken )) {
2178+
SET_REQ_WIN32_ERROR(req, GetLastError());
2179+
goto accesscheck_cleanup;
2180+
}
2181+
if (!DuplicateToken(hToken, SecurityImpersonation, &hImpersonatedToken)) {
2182+
SET_REQ_WIN32_ERROR(req, GetLastError());
2183+
goto accesscheck_cleanup;
2184+
}
21882185

2186+
/*
2187+
* Next, construct a mapping from generic access rights to
2188+
* the more specific access rights that AccessCheck expects.
2189+
*/
2190+
executeAccessRights = 0;
2191+
if (req->fs.info.mode & R_OK) {
2192+
executeAccessRights |= FILE_GENERIC_READ;
2193+
mapping.GenericRead = FILE_GENERIC_READ;
2194+
}
2195+
if (req->fs.info.mode & W_OK) {
2196+
executeAccessRights |= FILE_GENERIC_WRITE;
2197+
mapping.GenericWrite = FILE_GENERIC_WRITE;
2198+
}
2199+
if (req->fs.info.mode & X_OK) {
2200+
executeAccessRights |= FILE_GENERIC_EXECUTE;
2201+
mapping.GenericExecute = FILE_GENERIC_EXECUTE;
2202+
}
2203+
MapGenericMask(&executeAccessRights, &mapping);
2204+
2205+
privilegesLen = sizeof(privileges);
2206+
result = FALSE;
2207+
if (AccessCheck(sd,
2208+
hImpersonatedToken,
2209+
executeAccessRights,
2210+
&mapping,
2211+
&privileges,
2212+
&privilegesLen,
2213+
&grantedAccess,
2214+
&result)) {
21892215
/*
2190-
* Next we need to create an impersonation token representing
2191-
* the current user and the current process.
2192-
*/
2193-
tokenAccess = TOKEN_IMPERSONATE |
2194-
TOKEN_QUERY |
2195-
TOKEN_DUPLICATE |
2196-
STANDARD_RIGHTS_READ;
2197-
if (!OpenProcessToken(GetCurrentProcess(), tokenAccess, &hToken )) {
2198-
SET_REQ_WIN32_ERROR(req, GetLastError());
2199-
goto accesscheck_cleanup;
2200-
}
2201-
if (!DuplicateToken(hToken, SecurityImpersonation, &hImpersonatedToken)) {
2202-
SET_REQ_WIN32_ERROR(req, GetLastError());
2216+
* If AccessCheck passes, nothing went wrong, but
2217+
* we must still check that we have access.
2218+
*/
2219+
if (!result) {
2220+
SET_REQ_WIN32_ERROR(req, UV_EPERM);
22032221
goto accesscheck_cleanup;
22042222
}
2205-
2223+
} else {
22062224
/*
2207-
* Next, construct a mapping from generic access rights to
2208-
* the more specific access rights that AccessCheck expects.
2209-
*/
2210-
executeAccessRights = FILE_GENERIC_EXECUTE;
2211-
mapping.GenericExecute = FILE_GENERIC_EXECUTE;
2212-
MapGenericMask(&executeAccessRights, &mapping);
2213-
2214-
privilegesLen = sizeof(privileges);
2215-
result = FALSE;
2216-
if (AccessCheck(sd,
2217-
hImpersonatedToken,
2218-
executeAccessRights,
2219-
&mapping,
2220-
&privileges,
2221-
&privilegesLen,
2222-
&grantedAccess,
2223-
&result)) {
2224-
/*
2225-
* If AccessCheck passes, nothing went wrong, but
2226-
* we must still check that we have access.
2227-
*/
2228-
if (!result) {
2229-
SET_REQ_WIN32_ERROR(req, UV_EPERM);
2230-
goto accesscheck_cleanup;
2231-
}
2232-
} else {
2233-
/*
2234-
* This signifies that something went wrong with the
2235-
* actual AccessCheck() invocation itself.
2236-
*/
2237-
SET_REQ_WIN32_ERROR(req, GetLastError());
2238-
goto accesscheck_cleanup;
2239-
}
2225+
* This signifies that something went wrong with the
2226+
* actual AccessCheck() invocation itself.
2227+
*/
2228+
SET_REQ_WIN32_ERROR(req, GetLastError());
2229+
goto accesscheck_cleanup;
2230+
}
22402231

22412232
accesscheck_cleanup:
2242-
uv__free(sd);
2243-
if (hImpersonatedToken != NULL) {
2244-
CloseHandle(hImpersonatedToken);
2245-
}
2246-
if (hToken != NULL) {
2247-
CloseHandle(hToken);
2248-
}
2249-
/*
2250-
* If the result is false, return immediately.
2251-
* Some error code has been set in the `req` already.
2252-
*/
2253-
if (!result) {
2254-
return;
2255-
}
2233+
uv__free(sd);
2234+
if (hImpersonatedToken != NULL) {
2235+
CloseHandle(hImpersonatedToken);
2236+
}
2237+
if (hToken != NULL) {
2238+
CloseHandle(hToken);
2239+
}
2240+
/*
2241+
* If the result is false, return immediately.
2242+
* Some error code has been set in the `req` already.
2243+
*/
2244+
if (!result) {
2245+
return;
22562246
}
22572247

22582248
/* If we get to the end, everything worked out. */

0 commit comments

Comments
 (0)