diff --git a/deploy/windows/installer/ShopDBFlask.iss b/deploy/windows/installer/ShopDBFlask.iss index fa82b61..598118e 100644 --- a/deploy/windows/installer/ShopDBFlask.iss +++ b/deploy/windows/installer/ShopDBFlask.iss @@ -1206,6 +1206,12 @@ end; ; Delegate to the same tested script rather than duplicating removal logic here. ; It removes the site, app pool, firewall rule and application directory, and ; deliberately does NOT drop the database or uninstall MySQL. -Filename: "powershell.exe"; \ +; +; Sysnative, for the same reason as the [Run] entry above: the uninstaller is a +; 32-bit process, so a bare "powershell.exe" is resolved through WOW64 to the +; 32-bit PowerShell, which cannot see the IIS provider at all. Removal then +; skipped the site, application pool and application - leaving them pointing at +; a directory it HAD deleted - while Windows reported a clean uninstall. +Filename: "{win}\Sysnative\WindowsPowerShell\v1.0\powershell.exe"; \ Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\shopdb-install.ps1"" -Stage uninstall -BundleRoot ""{app}"" -AppRoot ""{app}"" -OnFailure never"; \ RunOnceId: "ShopDBFlaskUninstall"; Flags: waituntilterminated runhidden diff --git a/deploy/windows/installer/shopdb-admin.ps1 b/deploy/windows/installer/shopdb-admin.ps1 index 7702b74..3f24305 100644 --- a/deploy/windows/installer/shopdb-admin.ps1 +++ b/deploy/windows/installer/shopdb-admin.ps1 @@ -372,9 +372,28 @@ function Backup-Db { Head 'Database backup' $db = Get-DbParts if (-not $db) { Say ' no .env found - cannot determine the database' 'Red'; return } + $usingDefault = -not $Dest if (-not $Dest) { $Dest = 'C:\ProgramData\ShopDB-Flask\backups' } if (-not (Test-Path $Dest)) { New-Item -ItemType Directory -Path $Dest -Force | Out-Null } + # A dump holds every row, including the users table and its password hashes. + # A directory created under ProgramData INHERITS Users:RX, so those hashes + # were readable by every authenticated user on the server whenever this + # command created the directory rather than the installer. + # + # Re-applied on every backup, not only on creation, because this may be + # repairing a directory made by an earlier version. + # + # Only for the default location. A path the operator named is theirs, and + # silently rewriting its ACL is not this command's business - say so instead. + if ($usingDefault) { + & icacls.exe $Dest '/inheritance:r' ` + '/grant' 'BUILTIN\Administrators:(OI)(CI)(F)' ` + '/grant' 'NT AUTHORITY\SYSTEM:(OI)(CI)(F)' 2>&1 | Out-Null + } else { + Say ' note: this dump contains password hashes - check who can read that directory' 'Yellow' + } + $mysql = Find-MysqlClient if (-not $mysql) { Say ' mysql client not found' 'Red'; return } $dump = Join-Path (Split-Path $mysql -Parent) 'mysqldump.exe' diff --git a/deploy/windows/installer/shopdb-install.ps1 b/deploy/windows/installer/shopdb-install.ps1 index a1e060a..062e447 100644 --- a/deploy/windows/installer/shopdb-install.ps1 +++ b/deploy/windows/installer/shopdb-install.ps1 @@ -279,9 +279,18 @@ function Invoke-Native { function Protect-File { # Owner-only ACL: Administrators + SYSTEM, inheritance broken. - param([string] $Path) - Invoke-Native 'icacls.exe' @($Path,'/inheritance:r', - '/grant','BUILTIN\Administrators:(F)','/grant','NT AUTHORITY\SYSTEM:(F)') 'ACL' + # + # -Directory additionally makes the two grants INHERITABLE. Without (OI)(CI) + # the grants apply to the directory alone, so files written into it later - + # backups taken by the console, not by this script - are not covered. + param([string] $Path, [switch] $Directory) + $admins = 'BUILTIN\Administrators:(F)' + $system = 'NT AUTHORITY\SYSTEM:(F)' + if ($Directory) { + $admins = 'BUILTIN\Administrators:(OI)(CI)(F)' + $system = 'NT AUTHORITY\SYSTEM:(OI)(CI)(F)' + } + Invoke-Native 'icacls.exe' @($Path,'/inheritance:r','/grant',$admins,'/grant',$system) 'ACL' } function New-Secret { @@ -546,8 +555,13 @@ function Backup-Database { $dir = Join-Path $env:ProgramData 'ShopDB-Flask\backups' if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null - Protect-File $dir } + # RE-APPLIED EVERY TIME, not only on creation. The console creates this + # directory too (shopdb-admin.ps1 backup), and one created there inherits + # ProgramData's Users:RX - so dumps containing the users table and its + # password hashes became readable by every authenticated user on the server. + # The old create-only guard meant the installer could never repair that. + Protect-File $dir -Directory $file = Join-Path $dir ("{0}-{1}-{2}.sql" -f $db.Name, $Reason, (Get-Date -Format 'yyyyMMdd-HHmmss')) Write-Log "backing up $($db.Name) before migrating"