186 lines
7.2 KiB
PowerShell
186 lines
7.2 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[switch]$RequireDomainJoined,
|
|
[switch]$RequireRemoteAccess,
|
|
[switch]$RequireBrokerHealth,
|
|
[string]$RemoteDesktopPrincipal = 'LCI\SG-Laboratorio-Usuarios-RDP',
|
|
[switch]$Enforce
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}'
|
|
$passwordProviderClassId = '{60B78E88-EAD8-445C-9CFD-0B87F74EA6CD}'
|
|
$providerRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$providerClassId"
|
|
$passwordProviderRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$passwordProviderClassId"
|
|
$classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId\InprocServer32"
|
|
$defaultProviderPolicyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System'
|
|
$interactiveLogonPolicyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
|
$settingsPath = Join-Path $env:ProgramData 'SGU\CredentialProvider\settings.json'
|
|
$issues = [Collections.Generic.List[string]]::new()
|
|
|
|
$computer = Get-CimInstance Win32_ComputerSystem
|
|
if ($RequireDomainJoined -and -not $computer.PartOfDomain) {
|
|
$issues.Add('The computer is not joined to a domain.')
|
|
}
|
|
|
|
$providerRegistered = Test-Path -LiteralPath $providerRegistryPath
|
|
if (-not $providerRegistered) {
|
|
$issues.Add('The SGU Credential Provider registration is missing.')
|
|
}
|
|
|
|
$registeredDll = $null
|
|
if (Test-Path -LiteralPath $classRegistryPath) {
|
|
$registeredDll = (Get-Item -LiteralPath $classRegistryPath).GetValue('')
|
|
}
|
|
$providerBinaryPresent = $registeredDll -and (Test-Path -LiteralPath $registeredDll -PathType Leaf)
|
|
if (-not $providerBinaryPresent) {
|
|
$issues.Add('The registered SGU COM binary is missing.')
|
|
}
|
|
|
|
$configuredDefault = $null
|
|
try {
|
|
$configuredDefault = Get-ItemPropertyValue `
|
|
-LiteralPath $defaultProviderPolicyPath `
|
|
-Name DefaultCredentialProvider `
|
|
-ErrorAction Stop
|
|
}
|
|
catch {
|
|
# Report the missing or unreadable policy as a failed enrollment check.
|
|
}
|
|
$defaultProviderConfigured = $configuredDefault -eq $providerClassId
|
|
if (-not $defaultProviderConfigured) {
|
|
$issues.Add('The SGU provider is not assigned as the machine default credential provider.')
|
|
}
|
|
|
|
$lastSignedInUserHidden = $false
|
|
try {
|
|
$lastSignedInUserHidden = (Get-ItemPropertyValue `
|
|
-LiteralPath $interactiveLogonPolicyPath `
|
|
-Name DontDisplayLastUserName `
|
|
-ErrorAction Stop) -eq 1
|
|
}
|
|
catch {
|
|
# Report the missing or unreadable policy as a failed enrollment check.
|
|
}
|
|
if (-not $lastSignedInUserHidden) {
|
|
$issues.Add('The last signed-in user is not hidden from LogonUI.')
|
|
}
|
|
|
|
$localUserEnumerationDisabled = $false
|
|
try {
|
|
$localUserEnumerationDisabled = (Get-ItemPropertyValue `
|
|
-LiteralPath $defaultProviderPolicyPath `
|
|
-Name EnumerateLocalUsers `
|
|
-ErrorAction Stop) -eq 0
|
|
}
|
|
catch {
|
|
# Report the missing or unreadable policy as a failed enrollment check.
|
|
}
|
|
if (-not $localUserEnumerationDisabled) {
|
|
$issues.Add('Local user enumeration is not explicitly disabled for the domain client.')
|
|
}
|
|
|
|
$passwordProviderPreserved = Test-Path -LiteralPath $passwordProviderRegistryPath
|
|
if (-not $passwordProviderPreserved) {
|
|
$issues.Add('The built-in Microsoft password provider registration is missing.')
|
|
}
|
|
|
|
$settings = $null
|
|
try {
|
|
$settings = Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json
|
|
}
|
|
catch {
|
|
$issues.Add('The SGU provider settings file is missing or invalid.')
|
|
}
|
|
|
|
$clientCertificatePresent = $false
|
|
$serverCertificateTrusted = $false
|
|
$brokerHealth = $null
|
|
if ($settings) {
|
|
$clientCertificate = Get-ChildItem Cert:\LocalMachine\My |
|
|
Where-Object Thumbprint -eq $settings.ClientCertificateThumbprint |
|
|
Select-Object -First 1
|
|
$clientCertificatePresent = $clientCertificate -and $clientCertificate.HasPrivateKey
|
|
if (-not $clientCertificatePresent) {
|
|
$issues.Add('The client mTLS certificate with private key is missing.')
|
|
}
|
|
|
|
$serverCertificate = Get-ChildItem Cert:\LocalMachine\Root,Cert:\LocalMachine\CA |
|
|
Where-Object Thumbprint -eq $settings.ServerCertificateThumbprint |
|
|
Select-Object -First 1
|
|
$serverCertificateTrusted = [bool]$serverCertificate
|
|
if (-not $serverCertificateTrusted) {
|
|
$issues.Add('The broker certificate is not trusted by LocalMachine.')
|
|
}
|
|
|
|
if ($RequireBrokerHealth -and $clientCertificatePresent) {
|
|
try {
|
|
$healthUri = ([Uri]$settings.BrokerEndpoint).GetLeftPart([UriPartial]::Authority) + '/health/live'
|
|
$health = Invoke-RestMethod -Uri $healthUri -Certificate $clientCertificate -TimeoutSec 10
|
|
$brokerHealth = [string]$health.status
|
|
if ($brokerHealth -ne 'ok') {
|
|
$issues.Add('The broker health endpoint did not return ok.')
|
|
}
|
|
}
|
|
catch {
|
|
$issues.Add("The broker health check failed: $($_.Exception.Message)")
|
|
}
|
|
}
|
|
}
|
|
|
|
$dotNetRuntimePresent = $false
|
|
$dotnetCandidates = @(
|
|
(Get-Command dotnet -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source -ErrorAction SilentlyContinue),
|
|
(Join-Path $env:ProgramFiles 'dotnet\dotnet.exe')
|
|
) | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Leaf) } | Select-Object -Unique
|
|
foreach ($dotnet in $dotnetCandidates) {
|
|
if (& $dotnet --list-runtimes | Select-String '^Microsoft\.NETCore\.App 10\.') {
|
|
$dotNetRuntimePresent = $true
|
|
break
|
|
}
|
|
}
|
|
if (-not $dotNetRuntimePresent) {
|
|
$issues.Add('The Microsoft .NET 10 x64 runtime is missing.')
|
|
}
|
|
|
|
$remoteAccessReady = $null
|
|
if ($RequireRemoteAccess) {
|
|
$remoteDesktopUsersSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-555')
|
|
$remoteDesktopUsersGroup = ($remoteDesktopUsersSid.Translate([Security.Principal.NTAccount]).Value -split '\\', 2)[1]
|
|
$rdpMembers = @(Get-LocalGroupMember -Group $remoteDesktopUsersGroup -ErrorAction SilentlyContinue)
|
|
$remoteAccessReady =
|
|
(Get-Service TermService).Status -eq 'Running' -and
|
|
(Get-Service WinRM).Status -eq 'Running' -and
|
|
(Get-ItemPropertyValue 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections) -eq 0 -and
|
|
$rdpMembers.Name -contains $RemoteDesktopPrincipal
|
|
if (-not $remoteAccessReady) {
|
|
$issues.Add('RDP/WinRM or the authorized domain group is not fully configured.')
|
|
}
|
|
}
|
|
|
|
$result = [pscustomobject]@{
|
|
ComputerName = $env:COMPUTERNAME
|
|
Domain = $computer.Domain
|
|
DomainJoined = [bool]$computer.PartOfDomain
|
|
ProviderRegistered = $providerRegistered
|
|
ProviderBinary = $registeredDll
|
|
ProviderBinaryPresent = [bool]$providerBinaryPresent
|
|
DefaultProviderConfigured = $defaultProviderConfigured
|
|
LastSignedInUserHidden = $lastSignedInUserHidden
|
|
LocalUserEnumerationDisabled = $localUserEnumerationDisabled
|
|
PasswordProviderPreserved = $passwordProviderPreserved
|
|
SettingsPresent = [bool]$settings
|
|
ClientCertificatePresent = [bool]$clientCertificatePresent
|
|
ServerCertificateTrusted = $serverCertificateTrusted
|
|
DotNetRuntimePresent = $dotNetRuntimePresent
|
|
BrokerHealth = $brokerHealth
|
|
RemoteAccessReady = $remoteAccessReady
|
|
IsValid = $issues.Count -eq 0
|
|
Issues = $issues.ToArray()
|
|
}
|
|
|
|
$result
|
|
if ($Enforce -and -not $result.IsValid) {
|
|
throw "SGU client enrollment is invalid: $($issues -join ' ')"
|
|
}
|