Parsers/GPO/Registry.ps1
# Create a variable so we can set DependsOn values between passes. New-Variable -Name GlobalDependsOn -Value @() -Option AllScope -Scope Script -Force $GlobalDependsOn = @() Function Resolve-RegistrySpecialCases { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] $regHash ) # Special Cases. switch -regex ((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)) { "HKLM:\\System\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg\\(AllowedExactPaths|AllowedPaths)\\Machine" { # $regHash.ValueData = $regHash.ValueData -split "," } "HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\LegalNoticeText" { #Special case LegalNoticeText, written as a REG_MULTI_SZ by Group Policy Editor but written to registry as REG_SZ #Replacing comma with LF (line feed) and CR (Carriage Return) $values = $regHash.ValueData -split "," $regHash.ValueData = "" $values[0..($values.count-2)] | ForEach-Object{$regHash.ValueData += $_ +"`r`n"} $regHash.ValueData += $values[($values.count-1)] #Change the type to REG_SZ $regHash.ValueType = "String" } } } Function Add-RegistryDELVALDependsOn { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $regHash ) $DependsOn = @() $delVals = "DELVALS_$($regHash.Key.TrimStart("HKLM:"))" if ($script:GlobalDependsOn -contains $delVals -and $ExclusiveFlagAvailable) { $DependsOn += "[Registry]$delVals" } $delVal_ValueName = "DEL_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))" if ($script:GlobalDependsOn -contains $delVal_ValueName) { $DependsOn += "[Registry]$delVal_ValueName" } $deleteKeys = "DELETEKEY_$($regHash.Key.TrimStart("HKLM:"))" if ($script:GlobalDependsOn -contains $deleteKeys) { $DependsOn += "[Registry]$deleteKeys" } $deleteValue = "DELETEVALUES_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))" if ($script:GlobalDependsOn -contains $deleteValue) { $DependsOn += "[Registry]$deleteValue" } if ($DependsOn.count -gt 0) { $regHash.DependsOn = $DependsOn } } Function Register-RegistryDELVALDependsOn { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $regHash ) $CommentOUT = $false # Process any pol instructions. switch -regex ($regHash.ValueName) { "\*\*del\.(?<ValueName>.*)$" { $regHash.ValueName = $Matches.ValueName $regHash.Ensure = "Absent" $Name = "DEL_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))" $script:GlobalDependsOn += $Name # This is only a delete so return from here. return Write-DSCString -Resource -Name $Name -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT } "\*\*delvals\." { $regHash.Ensure = "Present" $regHash.ValueName = "" $regHash.Exclusive = $true $Name = "DELVALS_$($regHash.Key.TrimStart("HKLM:"))" $script:GlobalDependsOn += $Name # This is only a delete so return from here. return Write-DSCString -Resource -Name $Name -Type Registry -Parameters $regHash -CommentOUT:(!$ExclusiveFlagAvailable) } "\*\*DeleteValues" { $Data.ValueData = $Data.ValueData -replace "[^\u0020-\u007E]", "" foreach ($ValueName in ($Data.ValueData -split ";")) { $regHash.Ensure = "Absent" $Name = "DELETEVALUES_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))" $script:GlobalDependsOn += $Name # This is only a delete so return from here. Write-DSCString -Resource -Name $Name -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT } return } "\*\*DeleteKeys" { $Data.ValueData = $Data.ValueData -replace "[^\u0020-\u007E]", "" foreach ($Key in ($Data.ValueData -split ";")) { $regHash.Ensure = "Absent" $Name = "DELETEKEYS_$($regHash.Key.TrimStart("HKLM:"))" $script:GlobalDependsOn += $Name # This is only a delete so return from here. Write-DSCString -Resource -Name $Name -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT } return } "\*\*SecureKey" { $Name = "SECUREKEY_$($regHash.Key)" # This is only a delete so return from here. return Write-DSCString -Resource -Name $Name -Type Registry -Parameters $regHash -CommentOUT:$true } } return $null } Function Update-RegistryHashtable { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $regHash ) $regHash.ValueName = $regHash.ValueName -replace "[^\u0020-\u007E]", "" if ([string]::IsNullOrEmpty($regHash.ValueName)) { $regHash.Remove("ValueData") } $typeHash = @{"REG_SZ" = "String"; "REG_NONE" = "None"; "REG_DWORD" = "Dword"; "REG_EXPAND_SZ" = "ExpandString"; "REG_QWORD" = "Qword"; "REG_BINARY" = "Binary"; "REG_MULTI_SZ" = "MultiString"} if ($regHash.ValueType -in $typeHash.Keys) { $regHash.ValueType = $typeHash[$regHash.ValueType.ToString()] } else { $regHash.ValueType = "None" } if ($regHash.ContainsKey("ValueData") -and $regHash.ValueData -ne $null) { switch ($regHash.ValueType) { "String" { [string]$regHash.ValueData = "'$($regHash.ValueData)'"# -replace "[^\u0020-\u007E]", "" } "None" { $regHash.Remove("ValueData") | Out-Null } "ExpandString" { # Contains unexpanded Environment Paths. Should I expand them? [string]$regHash.ValueData = "'$($regHash.ValueData)'"# -replace "[^\u0020-\u007E]", "" } "Dword" { $ValueData = 1 if ($regHash.ValueData -match "(Disabled|Enabled|Not Defined|True|False)" -or $ValueData -eq "''") { # This is supposed to be an INT and it's a String [int]$regHash.ValueData = @{"Disabled" = 0; "Enabled" = 1; "Not Defined" = 0; "True" = 1; "False" = 0; '' = 0}.$ValueData } elseif (([int]::TryParse($regHash.ValueData, [ref]$ValueData))) { [int]$regHash.ValueData = $ValueData } else { # If it doesn't parse as an integer, try parsing as hexadecimal. Try { [int]$regHash.ValueData = [Convert]::ToInt32($regHash.ValueData, 16) } Catch { # Other wise fail over for now until a better option comes along. $regHash.Remove("ValueData") | Out-Null } } } "Qword" { } "Binary" { $reghash.ValueType = "Binary" if ($regHash.ContainsKey("ValueData")) { if ($regHash.ValueData.Count -gt 1) { Try { [string]$hexified = ($regHash.ValueData | ForEach-Object ToString X2) -join '' [string]$regHash.ValueData = $hexified } Catch { Write-Error "Error Processing Binary Data for Key ($(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName))" $regHash.CommentOut = $true Add-ProcessingHistory -Type Registry -Name "Registry(INF): $(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -ParsingError } } else { $regHash.ValueData = "$($regHash.ValueData)" } } $regHash.ValueType = "Binary" } "MultiString" { if ($regHash.ValueData -isnot [System.Array]) { # Does this have to be done in the Calling Function instead? $regHash.ValueData = @" $($regHash.ValueData) "@# -replace "[^\u0020-\u007E]", "" } $reghash.ValueType = "MultiString" } Default { $regHash.ValueType = "None" } } } if ($regHash.ValueType -eq "None") { # The REG_NONE is not allowed by the Registry resource. $regHash.Remove("ValueType") } if ([string]::IsNullOrEmpty($regHash.ValueName)) { $regHash.Remove("ValueData") } Resolve-RegistrySpecialCases $reghash } Function Write-GPORegistryXMLData { [CmdletBinding()] [OutputType([String])] param ( [Parameter(Mandatory = $true)] [System.Xml.XmlElement]$XML ) $regHash = @{} $regHash.ValueType = "None" $regHash.ValueName = "" $regHash.ValueData = "" $regHash.Key = "" $Properties = $XML.Properties $regHash.ValueData = $Properties.Value $regHash.ValueName = $Properties.name $regHash.ValueType = $Properties.Type $CommentOUT = $false switch ($Properties.hive) { "HKEY_LOCAL_MACHINE" { $regHash.Key = "HKLM:\" } "HKEY_CURRENT_USER" { Write-Warning "Write-GPORegistryXMLData: Current User Registry settings are not yet supported." $regHash.Key = "HKCU:\" $CommentOUT = $true } } $regHash.Key = Join-Path -Path $regHash.Key -ChildPath $Properties.Key Update-RegistryHashtable $regHash Write-DSCString -Resource -Name "Registry(XML): $(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT } Function Write-GPORegistryPOLData { [CmdletBinding()] [OutputType([String])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [psobject]$Data, [Parameter()] [ValidateSet("HKLM", "HKCU")] [string]$Hive = "HKLM" ) $regHash = @{} $regHash.ValueName = "" $regHash.Key = "$($Hive):\" $CommentOUT = $false if ($Hive -eq "HKCU") { Write-Warning "Write-GPORegistryPOLData: CurrentUser settings are currently not supported" $CommentOut = $true } $regHash.ValueName = $Data.ValueName $regHash.Key = Join-Path -Path $regHash.Key -ChildPath $Data.KeyName $regHash.ValueType = $Data.ValueType.ToString() if ($Data.ValueData -eq "$([char]0)") { $regHash.ValueData = $null } else { $regHash.ValueData = $Data.ValueData } Update-RegistryHashtable $regHash $output = Register-RegistryDELVALDependsOn $regHash if ($output -ne $null) { return $output } Add-RegistryDELVALDependsOn $regHash $comment = "" if ($regHash.ValueData -eq $null) { $Comment = "`tThis MultiString Value has a value of `$null, `n`tSome Security Policies require Registry Values to be `$null`n`tIf you believe ' ' is the correct value for this string, you may change it here." } Write-DSCString -Resource -Name "Registry(POL): $(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT -Comment $Comment } Function Write-GPORegistryINFData { [CmdletBinding()] [OutputType([String])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Key, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ValueData ) $regHash = @{} $regHash.ValueType = "None" $regHash.ValueName = "" $regHash.ValueData = "" $regHash.Key = "" $CommentOUT = $false Try { if ($ValueData -match "^(\d),") { $valueType = $Matches.1 $values = ($ValueData -split "^\d,")[1] $values = $values -replace '","', '&,' $values = $values -split '(?=[^&]),' for ($i = 0; $i -lt $values.count;$i++) { $values[$i] = $values[$i] -replace '&,', "," } $regHash.ValueData = $values } else { throw "Malformed data" } } catch { $regHash.ValueData = $null continue } $KeyPath = $Key $ValueName = Split-Path -Leaf $KeyPath $regHash.ValueName = $ValueName -replace "[^\u0020-\u007E]", "" $regHash.Key = Split-Path -Parent $KeyPath $regHash.Key = $regHash.Key -replace "MACHINE\\", "HKLM:\" if (!$regHash.Key.StartsWith("HKLM")) { Write-Warning "Write-GPORegistryINFData: Current User Registry settings are not yet supported." $CommentOUT = $true } $typeHash = @{"1" = "REG_SZ"; "7" = "REG_MULTI_SZ"; "4" = "REG_DWORD"; "3" = "REG_BINARY"} if ($typeHash.ContainsKey($valueType)) { $regHash.ValueType = $typeHash[$valueType] } else { Write-Warning "Write-GPORegistryINFData: $($values[0]) ValueType is not yet supported" # Add this resource to the processing history. Add-ProcessingHistory -Type Registry -Name "Registry(INF): $(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -ParsingError $CommentOUT = $true } Update-RegistryHashtable $regHash if ($regHash.ContainsKey("CommentOut")) { $CommentOUT = $true $regHash.Remove("CommentOut") } Write-DSCString -Resource -Name "Registry(INF): $(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT } |