diff --git a/src/cli/activation_transaction.c b/src/cli/activation_transaction.c index d9af2185b..101164e70 100644 --- a/src/cli/activation_transaction.c +++ b/src/cli/activation_transaction.c @@ -453,6 +453,52 @@ static bool activation_windows_owner_is_trusted(HANDLE handle) { return trusted; } +/* The SID Windows will stamp as OWNER on objects this process creates. + * + * That is TokenOwner, NOT TokenUser, and the two differ exactly when it matters: + * for a member of the Administrators group the default owner is + * BUILTIN\Administrators (the "System objects: Default owner for objects + * created by members of the Administrators group" policy, default on Server and + * common on hardened clients). So an elevated install created its staging file, + * then refused it as "owner-not-current-user" — we rejected a file we had just + * written ourselves (#1580), and the daemon path failed the same way, exiting + * before it could say anything (#1582). + * + * Reading TokenOwner is STRICTER-or-equal, never looser: it is the one SID this + * process stamps. On a non-elevated account TokenOwner == TokenUser and nothing + * changes. It does not accept "anything an administrator owns" — only the exact + * SID our own creations carry. */ +static bool activation_windows_token_owner(void **information_out, PSID *sid_out) { + *information_out = NULL; + *sid_out = NULL; + HANDLE token = NULL; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { + return false; + } + DWORD needed = 0; + (void)GetTokenInformation(token, TokenOwner, NULL, 0, &needed); + if (needed == 0 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + (void)CloseHandle(token); + return false; + } + void *information = calloc(1, needed); + bool ok = + information && GetTokenInformation(token, TokenOwner, information, needed, &needed) != 0; + (void)CloseHandle(token); + if (!ok) { + free(information); + return false; + } + PSID sid = ((TOKEN_OWNER *)information)->Owner; + if (!sid || !IsValidSid(sid)) { + free(information); + return false; + } + *information_out = information; + *sid_out = sid; + return true; +} + static bool activation_windows_owner_is_current(HANDLE handle) { void *information = NULL; PSID user_sid = NULL; @@ -464,6 +510,17 @@ static bool activation_windows_owner_is_current(HANDLE handle) { DWORD result = GetSecurityInfo(handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &owner, NULL, NULL, NULL, &descriptor); bool same = result == ERROR_SUCCESS && owner && IsValidSid(owner) && EqualSid(owner, user_sid); + /* Not our user SID — but Windows may legitimately have stamped our token's + * OWNER instead (Administrators, for an elevated account). Accept that and + * only that; see activation_windows_token_owner. */ + if (!same && result == ERROR_SUCCESS && owner && IsValidSid(owner)) { + void *owner_information = NULL; + PSID token_owner = NULL; + if (activation_windows_token_owner(&owner_information, &token_owner)) { + same = EqualSid(owner, token_owner) != 0; + free(owner_information); + } + } if (!same) { activation_note_refusal("owner-not-current-user", result); }