Skip to content

Commit 3b930bf

Browse files
committed
mingw: special-case administrators even more (#712)
The check for dubious ownership has one particular quirk on Windows: if running as an administrator, files owned by the Administrators _group_ are considered owned by the user. The rationale for that is: When running in elevated mode, Git creates files that aren't owned by the individual user but by the Administrators group. There is yet another quirk, though: The check I introduced to determine whether the current user is an administrator uses the `CheckTokenMembership()` function with the current process token. And that check only succeeds when running in elevated mode! Let's be a bit more lenient here and look harder whether the current user is an administrator. We do this by looking for a so-called "linked token". That token exists when administrators run in non-elevated mode, and can be used to create a new process in elevated mode. And feeding _that_ token to the `CheckTokenMembership()` function succeeds!
2 parents 715d246 + ce75f5b commit 3b930bf

File tree

2 files changed

+47
-11
lines changed

2 files changed

+47
-11
lines changed

compat/mingw.c

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3750,31 +3750,44 @@ static void setup_windows_environment(void)
37503750
has_symlinks = 0;
37513751
}
37523752

3753-
static PSID get_current_user_sid(void)
3753+
static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
37543754
{
37553755
HANDLE token;
37563756
DWORD len = 0;
3757-
PSID result = NULL;
3757+
TOKEN_ELEVATION_TYPE elevationType;
3758+
DWORD size;
3759+
3760+
*sid = NULL;
3761+
*linked_token = NULL;
37583762

37593763
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
3760-
return NULL;
3764+
return;
37613765

37623766
if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
37633767
TOKEN_USER *info = xmalloc((size_t)len);
37643768
if (GetTokenInformation(token, TokenUser, info, len, &len)) {
37653769
len = GetLengthSid(info->User.Sid);
3766-
result = xmalloc(len);
3767-
if (!CopySid(len, result, info->User.Sid)) {
3770+
*sid = xmalloc(len);
3771+
if (!CopySid(len, *sid, info->User.Sid)) {
37683772
error(_("failed to copy SID (%ld)"),
37693773
GetLastError());
3770-
FREE_AND_NULL(result);
3774+
FREE_AND_NULL(*sid);
37713775
}
37723776
}
37733777
FREE_AND_NULL(info);
37743778
}
3775-
CloseHandle(token);
37763779

3777-
return result;
3780+
if (GetTokenInformation(token, TokenElevationType, &elevationType, sizeof(elevationType), &size) &&
3781+
elevationType == TokenElevationTypeLimited) {
3782+
/*
3783+
* The current process is run by a member of the Administrators
3784+
* group, but is not running elevated.
3785+
*/
3786+
if (!GetTokenInformation(token, TokenLinkedToken, linked_token, sizeof(*linked_token), &size))
3787+
linked_token = NULL; /* there is no linked token */
3788+
}
3789+
3790+
CloseHandle(token);
37783791
}
37793792

37803793
static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
@@ -3853,18 +3866,22 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
38533866
if (err == ERROR_SUCCESS && sid && IsValidSid(sid)) {
38543867
/* Now, verify that the SID matches the current user's */
38553868
static PSID current_user_sid;
3869+
static HANDLE linked_token;
38563870
BOOL is_member;
38573871

38583872
if (!current_user_sid)
3859-
current_user_sid = get_current_user_sid();
3873+
get_current_user_sid(&current_user_sid, &linked_token);
38603874

38613875
if (current_user_sid &&
38623876
IsValidSid(current_user_sid) &&
38633877
EqualSid(sid, current_user_sid))
38643878
result = 1;
38653879
else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) &&
3866-
CheckTokenMembership(NULL, sid, &is_member) &&
3867-
is_member)
3880+
((CheckTokenMembership(NULL, sid, &is_member) &&
3881+
is_member) ||
3882+
(linked_token &&
3883+
CheckTokenMembership(linked_token, sid, &is_member) &&
3884+
is_member)))
38683885
/*
38693886
* If owned by the Administrators group, and the
38703887
* current user is an administrator, we consider that

t/helper/test-path-utils.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,25 @@ int cmd__path_utils(int argc, const char **argv)
504504
return !!res;
505505
}
506506

507+
if (argc > 1 && !strcmp(argv[1], "is_path_owned_by_current_user")) {
508+
int res = 0;
509+
510+
for (int i = 2; i < argc; i++) {
511+
struct strbuf buf = STRBUF_INIT;
512+
513+
if (is_path_owned_by_current_user(argv[i], &buf))
514+
printf("'%s' is owned by current SID\n", argv[i]);
515+
else {
516+
printf("'%s' is not owned by current SID: %s\n", argv[i], buf.buf);
517+
res = 1;
518+
}
519+
520+
strbuf_release(&buf);
521+
}
522+
523+
return res;
524+
}
525+
507526
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
508527
argv[1] ? argv[1] : "(there was none)");
509528
return 1;

0 commit comments

Comments
 (0)