From cc477ee897bac9af697d642430130be6f912fab8 Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Fri, 23 Jan 2026 15:24:29 -0800 Subject: [PATCH 1/2] Support non-elevated admin user check on Windows Currently, if a repository is owned by the Administrators group, libgit2 only considers the current user to be the owner if the process is running with elevated privileges. This change allows non-elevated processes run by users who are members of the Administrators group to also be considered the owner of such repositories. For the corresponding fix in git.exe see https://github.com/git/git/commit/03a4e46d122d5f24b6e1cd872eb996851c1563da --- src/util/fs_path.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/util/fs_path.c b/src/util/fs_path.c index ff0836ff8..5be2da35b 100644 --- a/src/util/fs_path.c +++ b/src/util/fs_path.c @@ -1853,12 +1853,16 @@ static PSID *sid_dup(PSID sid) return dup; } -static int current_user_sid(PSID *out) +static int current_user_sid(PSID *sid, HANDLE *linked_token) { TOKEN_USER *info = NULL; 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"); @@ -1879,9 +1883,19 @@ static int current_user_sid(PSID *out) goto done; } - if ((*out = sid_dup(info->User.Sid))) + 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; + } + } done: if (token) CloseHandle(token); @@ -1926,6 +1940,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; BOOL is_admin, admin_owned; int error; @@ -1938,7 +1953,7 @@ int git_fs_path_owner_is( goto done; if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0) { - if ((error = current_user_sid(&user_sid)) < 0) + if ((error = current_user_sid(&user_sid, &linked_token)) < 0) goto done; if (EqualSid(owner_sid, user_sid)) { @@ -1959,7 +1974,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(NULL, owner_sid, &is_admin) && + CheckTokenMembership(linked_token, owner_sid, &is_admin)) && is_admin) { *out = true; goto done; From f9f36a6fce8cef6fc912b3c2c219e1d8ab847115 Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Tue, 2 Jun 2026 08:10:00 +0200 Subject: [PATCH 2/2] 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 --- src/util/fs_path.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/util/fs_path.c b/src/util/fs_path.c index 5be2da35b..ad9ca797a 100644 --- a/src/util/fs_path.c +++ b/src/util/fs_path.c @@ -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;