diff --git a/windows/installer/oem/Configure-Kiosk.ps1 b/windows/installer/oem/Configure-Kiosk.ps1 index 552930a..97eff87 100644 --- a/windows/installer/oem/Configure-Kiosk.ps1 +++ b/windows/installer/oem/Configure-Kiosk.ps1 @@ -32,20 +32,35 @@ goto loop Log "wrote launcher $launcher" # --- Shell Launcher v2 (WMI bridge) --- +# WESL_UserSetting exposes STATIC methods on the CLASS — Get-CimInstance returns +# no instance, so every method must be called class-level (-Namespace/-ClassName), +# NOT via -InputObject on a (null) instance. Getting this wrong enables Shell +# Launcher with NO shell configured, which bricks EVERY logon (incl. OOBE's +# defaultuser0) into a reboot loop. So: set the DEFAULT shell to explorer.exe for +# all users first (this is what keeps OOBE/normal logons working), set the +# sm-bootstrap custom shell, and roll back SetEnabled(false) + fall back to a +# RunOnce launch if anything fails — never leave SL enabled-but-unconfigured. $cls='root\standardcimv2\embedded' -# Enable Shell Launcher FIRST, then fetch a fresh instance (the pre-enable -# snapshot's instance methods can silently no-op on some WESL builds). -Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$true} | Out-Null -$wesl=Get-CimInstance -Namespace $cls -ClassName WESL_UserSetting -ErrorAction Stop -# Default shell stays Explorer for everyone else. -Invoke-CimMethod -InputObject $wesl -MethodName SetDefaultShell -Arguments @{Shell='explorer.exe';DefaultAction=[uint32]0} | Out-Null -# sm-bootstrap => the elevating launcher; on exit, restart the shell (action 0). -Invoke-CimMethod -InputObject $wesl -MethodName SetCustomShell -Arguments @{ - Sid=(New-Object System.Security.Principal.NTAccount($BootstrapUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value - Shell="cmd.exe /c `"$launcher`"" - DefaultAction=[uint32]0 -} | Out-Null -Log 'shell launcher configured for sm-bootstrap' +$sid=(New-Object System.Security.Principal.NTAccount($BootstrapUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value +try { + Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$true} | Out-Null + # Default shell = Explorer for everyone else (incl. OOBE) — critical so non-kiosk logons don't break. + Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetDefaultShell -Arguments @{Shell='explorer.exe';DefaultAction=[uint32]0} | Out-Null + # sm-bootstrap => the elevating launcher; on exit, restart the shell (action 0). + Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetCustomShell -Arguments @{ + Sid=$sid; Shell="cmd.exe /c `"$launcher`""; DefaultAction=[uint32]0 } | Out-Null + $set=Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName GetCustomShell -Arguments @{Sid=$sid} -ErrorAction SilentlyContinue + if (-not $set -or [string]::IsNullOrEmpty($set.Shell)) { throw "custom shell did not take for $BootstrapUser" } + Log "shell launcher configured for sm-bootstrap (shell=$($set.Shell))" +} +catch { + Log "SHELL LAUNCHER CONFIG FAILED: $($_.Exception.Message) -- rolling back (SetEnabled false) + RunOnce fallback so onboarding still launches" + Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$false} -ErrorAction SilentlyContinue | Out-Null + # Fail-OPEN: no kiosk, but the Welcome wizard must still launch (we removed FirstLogonCommands). + $ro='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' + New-Item $ro -Force | Out-Null + Set-ItemProperty $ro -Name 'SilverOSWelcome' -Type String -Value "cmd /c powershell -NoProfile -ExecutionPolicy Bypass -Command `"Start-Process -LiteralPath '$welcomeEscaped' -Verb RunAs`"" +} # --- Keyboard Filter (block shell hotkeys) --- Enable-WindowsOptionalFeature -Online -FeatureName Client-KeyboardFilter -NoRestart -ErrorAction SilentlyContinue | Out-Null diff --git a/windows/welcome/src/SilverOS.Welcome.Core/Apply/BootstrapService.cs b/windows/welcome/src/SilverOS.Welcome.Core/Apply/BootstrapService.cs index e3dc9a7..e0fca60 100644 --- a/windows/welcome/src/SilverOS.Welcome.Core/Apply/BootstrapService.cs +++ b/windows/welcome/src/SilverOS.Welcome.Core/Apply/BootstrapService.cs @@ -7,15 +7,14 @@ public sealed class BootstrapService(IProcessRunner runner) : IBootstrapService // TearDownAsync so the sm-bootstrap SID still resolves. public async Task RevertKioskAsync(CancellationToken ct = default) { - // Remove sm-bootstrap custom shell entry + disable Shell Launcher's per-user entry. + // Remove sm-bootstrap custom shell entry + disable Shell Launcher. WESL_UserSetting + // methods are STATIC on the class — call class-level (-Namespace/-ClassName), NOT + // via -InputObject on a Get-CimInstance result (which is always null). await Ps( "$c='root\\\\standardcimv2\\\\embedded';" + - "$w=Get-CimInstance -Namespace $c -ClassName WESL_UserSetting -EA SilentlyContinue;" + - "if($w){" + - "$sid=(New-Object System.Security.Principal.NTAccount('sm-bootstrap')).Translate([System.Security.Principal.SecurityIdentifier]).Value;" + - "Invoke-CimMethod -InputObject $w -MethodName RemoveCustomShell -Arguments @{Sid=$sid} -EA SilentlyContinue | Out-Null;" + - "Invoke-CimMethod -InputObject $w -MethodName SetEnabled -Arguments @{Enabled=$false} -EA SilentlyContinue | Out-Null" + - "}", + "$sid=(New-Object System.Security.Principal.NTAccount('sm-bootstrap')).Translate([System.Security.Principal.SecurityIdentifier]).Value;" + + "Invoke-CimMethod -Namespace $c -ClassName WESL_UserSetting -MethodName RemoveCustomShell -Arguments @{Sid=$sid} -EA SilentlyContinue | Out-Null;" + + "Invoke-CimMethod -Namespace $c -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$false} -EA SilentlyContinue | Out-Null", ct); // Revert escape policies set by Configure-Kiosk.ps1. await Ps(