Extend Win32 safe.directory admin-owner check to also cover non-elevated processes

The current implementation which checks whether the current user is in the administrators group only works if the process runs elevated.

Another check using a so-called "linked token" is necessary which exists when administrators run in non-elevated mode.

Based on Git commit 03a4e46d122d5f24b6e1cd872eb996851c1563da.

Signed-off-by: Sven Strickroth <email@cs-ware.de>
This commit is contained in:
Sven Strickroth
2026-06-02 08:10:00 +02:00
committed by Edward Thomson
parent cc477ee897
commit f9f36a6fce

View File

@@ -1856,14 +1856,13 @@ static PSID *sid_dup(PSID sid)
static int current_user_sid(PSID *sid, HANDLE *linked_token)
{
TOKEN_USER *info = NULL;
TOKEN_ELEVATION_TYPE elevationType = 0;
HANDLE token = NULL;
DWORD len = 0;
int error = -1;
TOKEN_ELEVATION_TYPE elevation_type;
DWORD size;
*linked_token = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
git_error_set(GIT_ERROR_OS, "could not lookup process information");
goto done;
@@ -1883,19 +1882,21 @@ static int current_user_sid(PSID *sid, HANDLE *linked_token)
goto done;
}
if ((*sid = sid_dup(info->User.Sid)))
error = 0;
if (GetTokenInformation(token, TokenElevationType, &elevation_type, sizeof(elevation_type), &size) &&
elevation_type == TokenElevationTypeLimited) {
/*
* The current process is run by a member of the Administrators group
* but is not running elevated.
*/
if (!GetTokenInformation(token, TokenLinkedToken, linked_token, sizeof(HANDLE), &size)) {
linked_token = NULL;
if (!GetTokenInformation(token, TokenLinkedToken, linked_token, sizeof(HANDLE), &size)) {
CloseHandle(*linked_token);
*linked_token = NULL;
}
}
if ((*sid = sid_dup(info->User.Sid)))
error = 0;
done:
if (token)
CloseHandle(token);
@@ -1940,7 +1941,7 @@ int git_fs_path_owner_is(
git_fs_path_owner_t owner_type)
{
PSID owner_sid = NULL, user_sid = NULL;
static HANDLE linked_token;
HANDLE linked_token = NULL;
BOOL is_admin, admin_owned;
int error;
@@ -1949,17 +1950,14 @@ int git_fs_path_owner_is(
return 0;
}
if ((error = file_owner_sid(&owner_sid, path)) < 0)
if ((error = file_owner_sid(&owner_sid, path)) < 0 ||
(error = current_user_sid(&user_sid, &linked_token)) < 0)
goto done;
if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0) {
if ((error = current_user_sid(&user_sid, &linked_token)) < 0)
goto done;
if (EqualSid(owner_sid, user_sid)) {
*out = true;
goto done;
}
if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0 &&
EqualSid(owner_sid, user_sid)) {
*out = true;
goto done;
}
admin_owned =
@@ -1974,8 +1972,8 @@ int git_fs_path_owner_is(
if (admin_owned &&
(owner_type & GIT_FS_PATH_USER_IS_ADMINISTRATOR) != 0 &&
(CheckTokenMembership(NULL, owner_sid, &is_admin) &&
CheckTokenMembership(linked_token, owner_sid, &is_admin)) &&
CheckTokenMembership(NULL, owner_sid, &is_admin) &&
CheckTokenMembership(linked_token, owner_sid, &is_admin) &&
is_admin) {
*out = true;
goto done;
@@ -1984,6 +1982,9 @@ int git_fs_path_owner_is(
*out = false;
done:
if (linked_token)
CloseHandle(linked_token);
git__free(owner_sid);
git__free(user_sid);
return error;