fix(windows): accept the owner Windows actually stamps on what we create

An elevated install created its own staging file and then refused it:

    error: failed to stage install candidate: activation transaction I/O
    failed: owner-not-current-user

We compared the object's owner against TokenUser. Windows stamps newly created
objects with TokenOwner, and for a member of the Administrators group that
defaults to BUILTIN\Administrators ("System objects: Default owner for objects
created by members of the Administrators group" — default on Server, common on
hardened clients). So on any elevated account the check rejected a file written
seconds earlier by the same process. Reported by @spam0115 (#1580); @gotspatel
hit the same wall from the other side (#1582) — a fresh install whose MCP server
exits during initialize, from `C:\Users\Administrator`.

The fix reads TokenOwner and accepts that SID too. This is stricter-or-equal,
never looser: TokenOwner is the ONE SID this process stamps on its own
creations, so the check still refuses anything a different account owns. It does
NOT accept "whatever an administrator owns" — that would be the loose version,
and the neighbouring activation_windows_owner_is_trusted already covers the
pre-existing-directory case where a broader rule is appropriate. On a
non-elevated account TokenOwner == TokenUser and nothing changes at all.

Windows-only code, so it is unverifiable on the macOS host: this needs the
Windows leg to compile and exercise, which is the next step before merge.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel committed 2026-08-13 10:04:55 +02:00
1 parent 0c64de93a2
commit 4c793c4ded
1 file changed
+57
+57
View File
@@ -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);
}