fix(installer): give the Windows staging directory an owner-only DACL
install.ps1 created its staging directory with New-Item, which inherits whatever %TEMP% carries. It then ran the downloaded binary from that directory, and the binary validates its own directory and refuses inherited cross-account mutation grants. So the installer handed our own validator a directory the installer had made wrong: error: failed to stage install candidate: activation transaction I/O failed: acl-grants-cross-account-mutation to S-1-5-21-...-1003 Five reporters, three different offending identities - a CodexSandboxUsers local group, a synthesized SID recorded in .codex/cap_sid, and orphaned SIDs left by uninstalled software - all naming an ACE the installer inherited rather than anything cbm wrote. Still live on v0.10.4 (#1614). Closes #1529, #1614, #1571. cbm's own C staging already creates its directory with a protected owner-only DACL (win_mkdtemp_private_create). install.ps1 was the single path that skipped it, which is exactly why redirecting TMP/TEMP to a fresh directory worked around the failure - that workaround is now unnecessary. Applied after creation rather than atomically on purpose: the CreateDirectory overload taking a DirectorySecurity exists on Windows PowerShell 5.1 but not on PowerShell 7, and Set-Acl works on both. The directory name is unpredictable and nothing is written into it before the download, so the window is not usefully attackable. Best-effort by design: a filesystem that cannot carry a DACL must not fail the install, and if the hardening does not take, the binary's own validation still refuses - the honest outcome rather than a silent downgrade. Pure ASCII, as PowerShell 5.1 decodes a BOM-less .ps1 as ANSI. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
+35
@@ -128,6 +128,41 @@ $Url = "$BaseUrl/$Archive"
|
||||
$TmpDir = Join-Path ([System.IO.Path]::GetTempPath()) "cbm-install-$(Get-Random)"
|
||||
New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
|
||||
|
||||
# Give the staging directory a protected owner-only DACL.
|
||||
#
|
||||
# Without this it inherits whatever %TEMP% carries, and the binary we are about
|
||||
# to run from here validates its own directory and refuses inherited
|
||||
# cross-account mutation grants. That is not a hypothetical: sandboxed clients
|
||||
# leave ACEs on %TEMP% (a CodexSandboxUsers group, AppContainer SIDs, and
|
||||
# orphaned SIDs from uninstalled software have all been reported), and installs
|
||||
# failed with
|
||||
# activation transaction I/O failed: acl-grants-cross-account-mutation to S-1-5-21-...
|
||||
# naming an ACE the installer itself inherited. See issues 1529, 1614 and 1571.
|
||||
#
|
||||
# cbm's own C staging already creates its directory this way; install.ps1 was
|
||||
# the one path that did not, which is why redirecting TMP/TEMP worked around it.
|
||||
#
|
||||
# Applied after creation rather than atomically on purpose: the overload that
|
||||
# takes a DirectorySecurity exists on Windows PowerShell 5.1 but not on
|
||||
# PowerShell 7, and Set-Acl works on both. The directory name is unpredictable
|
||||
# and nothing is written into it until the download below, so the window is not
|
||||
# usefully attackable.
|
||||
#
|
||||
# Best-effort: a filesystem that cannot carry a DACL must not fail the install.
|
||||
# If this does not take, the binary's own validation still refuses to proceed,
|
||||
# which is the honest outcome rather than a silent downgrade.
|
||||
try {
|
||||
$stagingAcl = New-Object System.Security.AccessControl.DirectorySecurity
|
||||
$stagingAcl.SetAccessRuleProtection($true, $false)
|
||||
$stagingOwner = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User
|
||||
$stagingAcl.SetOwner($stagingOwner)
|
||||
$stagingAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule(
|
||||
$stagingOwner, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')))
|
||||
Set-Acl -Path $TmpDir -AclObject $stagingAcl -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Host "note: could not harden the staging directory ACL: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
Write-Host "Downloading $Archive..."
|
||||
try {
|
||||
Invoke-CbmDownload -Url $Url -OutFile "$TmpDir\$Archive"
|
||||
|
||||
Reference in New Issue
Block a user