Collectors/Get-EscPkiObjectAcl.ps1
|
function Get-EscPkiObjectAcl { <# .SYNOPSIS Collects nTSecurityDescriptor ACLs of key AD CS objects (read-only). .DESCRIPTION Reads the security descriptor of: - NTAuthCertificates (CN=NTAuthCertificates,CN=Public Key Services,...) - OID container (CN=OID,CN=Public Key Services,...) - each Enrollment Service (pKIEnrollmentService) - each CA object (certificationAuthority under CN=Certification Authorities) - each CA computer object (matched by dNSHostName) and maps them to PkiAcl schema (ObjectType, DN, OwnerSid, Aces[]). Offline mode: pass -InputObject with records of the shape @{ ObjectType=..; DN=..; SecurityDescriptor=<base64|bytes> } .PARAMETER Server Optional DC/server to bind to. .PARAMETER ConfigurationNamingContext Optional config NC DN (auto-detected from RootDSE when omitted). .PARAMETER InputObject Optional array of offline records (see description). .PARAMETER ExtraLowPrivSid Optional extra low-priv SIDs forwarded to the ACL parser. .OUTPUTS [pscustomobject] PkiAcl[]. .EXAMPLE Get-EscPkiObjectAcl -Verbose #> [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string] $Server, [Parameter(Mandatory = $false)] [string] $ConfigurationNamingContext, [Parameter(Mandatory = $false)] [object[]] $InputObject, [Parameter(Mandatory = $false)] [string[]] $ExtraLowPrivSid = @() ) if ($PSBoundParameters.ContainsKey('InputObject') -and $null -ne $InputObject) { Write-EscLog -Component 'PkiAcl' -Message ("Offline mode: transforming {0} fixture record(s)." -f @($InputObject).Count) $out = @() foreach ($rec in $InputObject) { $sd = $rec.SecurityDescriptor $out += ConvertTo-EscPkiAclObject -ObjectType ([string]$rec.ObjectType) -DN ([string]$rec.DN) -SecurityDescriptor $sd -ExtraLowPrivSid $ExtraLowPrivSid } return @($out) } $configNc = $ConfigurationNamingContext if ([string]::IsNullOrWhiteSpace($configNc)) { $configNc = Get-EscConfigNamingContext -Server $Server } if ([string]::IsNullOrWhiteSpace($configNc)) { Write-EscLog -Component 'PkiAcl' -Level Warning -Message 'Could not resolve configuration naming context; returning empty set.' return @() } $sdProps = @('distinguishedName', 'nTSecurityDescriptor', 'dNSHostName') $out = @() $emitOne = { param($dn, $objType) $res = Invoke-EscLdapSearch -SearchRoot $dn -Filter '(objectClass=*)' -Server $Server ` -PropertiesToLoad $sdProps -SearchScope 'Base' if (@($res).Count -gt 0) { $bag = ConvertTo-EscPropertyBag -SearchResult $res[0] $sd = $null if ($bag.ContainsKey('ntsecuritydescriptor')) { $v = $bag['ntsecuritydescriptor'] if ($v -is [System.Array] -and $v.Length -gt 0) { $sd = $v[0] } else { $sd = $v } } return (ConvertTo-EscPkiAclObject -ObjectType $objType -DN $dn -SecurityDescriptor $sd -ExtraLowPrivSid $ExtraLowPrivSid) } return $null } $ntauthDn = 'CN=NTAuthCertificates,CN=Public Key Services,CN=Services,{0}' -f $configNc $o = & $emitOne $ntauthDn 'NTAuthCertificates' if ($null -ne $o) { $out += $o } $oidDn = 'CN=OID,CN=Public Key Services,CN=Services,{0}' -f $configNc $o = & $emitOne $oidDn 'OidContainer' if ($null -ne $o) { $out += $o } $enrollRoot = 'CN=Enrollment Services,CN=Public Key Services,CN=Services,{0}' -f $configNc $enrollResults = Invoke-EscLdapSearch -SearchRoot $enrollRoot -Filter '(objectClass=pKIEnrollmentService)' ` -Server $Server -PropertiesToLoad $sdProps -SearchScope 'OneLevel' $caHostNames = @() foreach ($r in $enrollResults) { $bag = ConvertTo-EscPropertyBag -SearchResult $r $dn = '' if ($bag.ContainsKey('distinguishedname')) { $dn = [string] @($bag['distinguishedname'])[0] } $sd = $null if ($bag.ContainsKey('ntsecuritydescriptor')) { $sd = @($bag['ntsecuritydescriptor'])[0] } if (-not [string]::IsNullOrWhiteSpace($dn)) { $out += ConvertTo-EscPkiAclObject -ObjectType 'EnrollmentService' -DN $dn -SecurityDescriptor $sd -ExtraLowPrivSid $ExtraLowPrivSid } if ($bag.ContainsKey('dnshostname')) { $h = [string] @($bag['dnshostname'])[0] if (-not [string]::IsNullOrWhiteSpace($h)) { $caHostNames += $h } } } $caRoot = 'CN=Certification Authorities,CN=Public Key Services,CN=Services,{0}' -f $configNc $caResults = Invoke-EscLdapSearch -SearchRoot $caRoot -Filter '(objectClass=certificationAuthority)' ` -Server $Server -PropertiesToLoad $sdProps -SearchScope 'OneLevel' foreach ($r in $caResults) { $bag = ConvertTo-EscPropertyBag -SearchResult $r $dn = '' if ($bag.ContainsKey('distinguishedname')) { $dn = [string] @($bag['distinguishedname'])[0] } $sd = $null if ($bag.ContainsKey('ntsecuritydescriptor')) { $sd = @($bag['ntsecuritydescriptor'])[0] } if (-not [string]::IsNullOrWhiteSpace($dn)) { $out += ConvertTo-EscPkiAclObject -ObjectType 'CaObject' -DN $dn -SecurityDescriptor $sd -ExtraLowPrivSid $ExtraLowPrivSid } } $defaultNc = Get-EscDefaultNamingContext -Server $Server if (-not [string]::IsNullOrWhiteSpace($defaultNc)) { foreach ($caHost in ($caHostNames | Select-Object -Unique)) { $filter = '(&(objectClass=computer)(dNSHostName={0}))' -f $caHost $compRes = Invoke-EscLdapSearch -SearchRoot $defaultNc -Filter $filter -Server $Server ` -PropertiesToLoad $sdProps -SearchScope 'Subtree' foreach ($r in $compRes) { $bag = ConvertTo-EscPropertyBag -SearchResult $r $dn = '' if ($bag.ContainsKey('distinguishedname')) { $dn = [string] @($bag['distinguishedname'])[0] } $sd = $null if ($bag.ContainsKey('ntsecuritydescriptor')) { $sd = @($bag['ntsecuritydescriptor'])[0] } if (-not [string]::IsNullOrWhiteSpace($dn)) { $out += ConvertTo-EscPkiAclObject -ObjectType 'CaComputer' -DN $dn -SecurityDescriptor $sd -ExtraLowPrivSid $ExtraLowPrivSid } } } } else { Write-EscLog -Component 'PkiAcl' -Level Warning -Message 'Could not resolve default naming context; skipping CA computer ACLs.' } Write-EscLog -Component 'PkiAcl' -Message ("Collected {0} PKI object ACL record(s)." -f @($out).Count) return @($out) } |