Common.ps1
#requires -Version 2.0 function OpenPolicyFile { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Path ) $policyFile = New-Object TJX.PolFileEditor.PolFile $policyFile.FileName = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path) if (Test-Path -LiteralPath $policyFile.FileName) { try { $policyFile.LoadFile() } catch { $errorRecord = $_ $message = "Error loading policy file at path '$Path': $($errorRecord.Exception.Message)" $exception = New-Object System.Exception($message, $errorRecord.Exception) throw $exception } } return $policyFile } function PolEntryToPsObject { param ( [TJX.PolFileEditor.PolEntry] $PolEntry ) $type = PolEntryTypeToRegistryValueKind $PolEntry.Type $data = GetEntryData -Entry $PolEntry -Type $type return New-Object psobject -Property @{ Key = $PolEntry.KeyName ValueName = $PolEntry.ValueName Type = $type Data = $data } } function GetEntryData { param ( [TJX.PolFileEditor.PolEntry] $Entry, [Microsoft.Win32.RegistryValueKind] $Type ) switch ($type) { ([Microsoft.Win32.RegistryValueKind]::Binary) { return $Entry.BinaryValue } ([Microsoft.Win32.RegistryValueKind]::DWord) { return $Entry.DWORDValue } ([Microsoft.Win32.RegistryValueKind]::ExpandString) { return $Entry.StringValue } ([Microsoft.Win32.RegistryValueKind]::MultiString) { return $Entry.MultiStringValue } ([Microsoft.Win32.RegistryValueKind]::QWord) { return $Entry.QWORDValue } ([Microsoft.Win32.RegistryValueKind]::String) { return $Entry.StringValue } } } function SavePolicyFile { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [TJX.PolFileEditor.PolFile] $PolicyFile, [switch] $UpdateGptIni ) if ($PSCmdlet.ShouldProcess($PolicyFile.FileName, 'Save new settings')) { $parentPath = Split-Path $PolicyFile.FileName -Parent if (-not (Test-Path -LiteralPath $parentPath -PathType Container)) { try { $null = New-Item -Path $parentPath -ItemType Directory -ErrorAction Stop -Confirm:$false -WhatIf:$false } catch { $errorRecord = $_ $message = "Error creating parent folder of path '$Path': $($errorRecord.Exception.Message)" $exception = New-Object System.Exception($message, $errorRecord.Exception) throw $exception } } try { $PolicyFile.SaveFile() } catch { $errorRecord = $_ $message = "Error saving policy file to path '$($PolicyFile.FileName)': $($errorRecord.Exception.Message)" $exception = New-Object System.Exception($message, $errorRecord.Exception) throw $exception } } if ($UpdateGptIni) { if ($policyFile.FileName -match '^(.*)\\+([^\\]+)\\+[^\\]+$' -and $Matches[2] -eq 'User' -or $Matches[2] -eq 'Machine') { $iniPath = Join-Path $Matches[1] GPT.ini if (Test-Path -LiteralPath $iniPath -PathType Leaf) { if ($PSCmdlet.ShouldProcess($iniPath, 'Increment version number in INI file')) { IncrementGptIniVersion -Path $iniPath -PolicyType $Matches[2] -Confirm:$false -WhatIf:$false } } else { if ($PSCmdlet.ShouldProcess($iniPath, 'Create new gpt.ini file')) { NewGptIni -Path $iniPath -PolicyType $Matches[2] } } } } } function NewGptIni { param ( [string] $Path, [string[]] $PolicyType ) $parent = Split-Path $Path -Parent if (-not (Test-Path $parent -PathType Container)) { $null = New-Item -Path $parent -ItemType Directory -ErrorAction Stop } $version = GetNewVersionNumber -Version 0 -PolicyType $PolicyType Set-Content -Path $Path -Encoding Ascii -Value @" [General] gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}] Version=$version gPCUserExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}] "@ } function IncrementGptIniVersion { [CmdletBinding(SupportsShouldProcess = $true)] param ( [string] $Path, [string[]] $PolicyType ) $foundVersionLine = $false $section = '' $newContents = @( foreach ($line in Get-Content $Path) { # This might not be the most unreadable regex ever, but it's trying hard to be! # It's looking for section lines: [SectionName] if ($line -match '^\s*\[([^\]]+)\]\s*$') { if ($section -eq 'General') { if (-not $foundVersionLine) { $foundVersionLine = $true $newVersion = GetNewVersionNumber -Version 0 -PolicyType $PolicyType "Version=$newVersion" } if (-not $foundMachineExtensionLine) { $foundMachineExtensionLine = $true 'gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]' } if (-not $foundUserExtensionLine) { $foundUserExtensionLine = $true 'gPCUserExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F73-3407-48AE-BA88-E8213C6761F1}]' } } $section = $matches[1] } elseif ($section -eq 'General' -and $line -match '^\s*Version\s*=\s*(\d+)\s*$' -and $null -ne ($version = $matches[1] -as [uint32])) { $foundVersionLine = $true $newVersion = GetNewVersionNumber -Version $version -PolicyType $PolicyType $line = "Version=$newVersion" } elseif ($section -eq 'General' -and $line -match '^\s*gPC(Machine|User)ExtensionNames\s*=') { if ($matches[1] -eq 'Machine') { $foundMachineExtensionLine = $true } else { $foundUserExtensionLine = $true } $line = EnsureAdminTemplateCseGuidsArePresent $line } $line } if ($section -eq 'General') { if (-not $foundVersionLine) { $foundVersionLine = $true $newVersion = GetNewVersionNumber -Version 0 -PolicyType $PolicyType "Version=$newVersion" } if (-not $foundMachineExtensionLine) { $foundMachineExtensionLine = $true 'gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]' } if (-not $foundUserExtensionLine) { $foundUserExtensionLine = $true 'gPCUserExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F73-3407-48AE-BA88-E8213C6761F1}]' } } ) if ($PSCmdlet.ShouldProcess($Path, 'Increment Version number')) { Set-Content -Path $Path -Value $newContents -Encoding Ascii -Confirm:$false -WhatIf:$false } } function EnsureAdminTemplateCseGuidsArePresent { param ([string] $Line) # These lines contain lists of GUIDs in "registry" format (with the curly braces), separated by nothing, wrapped in a pair of square brackets. Ex: # gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}] # Per Darren Mar-Elia, these GUIDs must be in alphabetical order, or GP processing will have problems. if ($Line -notmatch '\s*(gPC(?:Machine|User)ExtensionNames)\s*=\s*\[([^\]]*)\]\s*$') { throw "Malformed gpt.ini line: $Line" } $valueName = $matches[1] $guidStrings = @($matches[2] -split '(?<=})(?={)') $guidList = @( $guidStrings '{35378EAC-683F-11D2-A89A-00C04FBBCFA2}' '{D02B1F72-3407-48AE-BA88-E8213C6761F1}' ) $newGuidString = ($guidList | Sort-Object -Unique) -join '' return "$valueName=[$newGuidString]" } function GetNewVersionNumber { param ( [UInt32] $Version, [string[]] $PolicyType ) # User version is the high 16 bits, Machine version is the low 16 bits. # Reference: http://blogs.technet.com/b/grouppolicy/archive/2007/12/14/understanding-the-gpo-version-number.aspx $pair = UInt32ToUInt16Pair -UInt32 $version if ($PolicyType -contains 'User') { $pair.HighPart++ } if ($PolicyType -contains 'Machine') { $pair.LowPart++ } return UInt16PairToUInt32 -UInt16Pair $pair } function UInt32ToUInt16Pair { param ([UInt32] $UInt32) # Deliberately avoiding bitwise shift operators here, for PowerShell v2 compatibility. $lowPart = $UInt32 -band 0xFFFF $highPart = ($UInt32 - $lowPart) / 0x10000 return New-Object psobject -Property @{ LowPart = [UInt16] $lowPart HighPart = [UInt16] $highPart } } function UInt16PairToUInt32 { param ([object] $UInt16Pair) # Deliberately avoiding bitwise shift operators here, for PowerShell v2 compatibility. return ([UInt32] $UInt16Pair.HighPart) * 0x10000 + $UInt16Pair.LowPart } function PolEntryTypeToRegistryValueKind { param ([TJX.PolFileEditor.PolEntryType] $PolEntryType) switch ($PolEntryType) { ([TJX.PolFileEditor.PolEntryType]::REG_NONE) { return [Microsoft.Win32.RegistryValueKind]::None } ([TJX.PolFileEditor.PolEntryType]::REG_DWORD) { return [Microsoft.Win32.RegistryValueKind]::DWord } ([TJX.PolFileEditor.PolEntryType]::REG_DWORD_BIG_ENDIAN) { return [Microsoft.Win32.RegistryValueKind]::DWord } ([TJX.PolFileEditor.PolEntryType]::REG_BINARY) { return [Microsoft.Win32.RegistryValueKind]::Binary } ([TJX.PolFileEditor.PolEntryType]::REG_EXPAND_SZ) { return [Microsoft.Win32.RegistryValueKind]::ExpandString } ([TJX.PolFileEditor.PolEntryType]::REG_MULTI_SZ) { return [Microsoft.Win32.RegistryValueKind]::MultiString } ([TJX.PolFileEditor.PolEntryType]::REG_QWORD) { return [Microsoft.Win32.RegistryValueKind]::QWord } ([TJX.PolFileEditor.PolEntryType]::REG_SZ) { return [Microsoft.Win32.RegistryValueKind]::String } } } function GetPolFilePath { param ( [Parameter(Mandatory = $true, ParameterSetName = 'PolicyType')] [string] $PolicyType, [Parameter(Mandatory = $true, ParameterSetName = 'Account')] [string] $Account ) if ($PolicyType) { switch ($PolicyType) { 'Machine' { return Join-Path $env:SystemRoot System32\GroupPolicy\Machine\registry.pol } 'User' { return Join-Path $env:SystemRoot System32\GroupPolicy\User\registry.pol } 'Administrators' { # BUILTIN\Administrators well-known SID return Join-Path $env:SystemRoot System32\GroupPolicyUsers\S-1-5-32-544\User\registry.pol } 'NonAdministrators' { # BUILTIN\Users well-known SID return Join-Path $env:SystemRoot System32\GroupPolicyUsers\S-1-5-32-545\User\registry.pol } } } else { try { $sid = $Account -as [System.Security.Principal.SecurityIdentifier] if ($null -eq $sid) { $sid = GetSidForAccount $Account } return Join-Path $env:SystemRoot "System32\GroupPolicyUsers\$($sid.Value)\User\registry.pol" } catch { throw } } } function GetSidForAccount($Account) { $acc = $Account if ($acc -notlike '*\*') { $acc = "$env:COMPUTERNAME\$acc" } try { $ntAccount = [System.Security.Principal.NTAccount]$acc return $ntAccount.Translate([System.Security.Principal.SecurityIdentifier]) } catch { $message = "Could not translate account '$acc' to a security identifier." $exception = New-Object System.Exception($message, $_.Exception) $errorRecord = New-Object System.Management.Automation.ErrorRecord( $exception, 'CouldNotGetSidForAccount', [System.Management.Automation.ErrorCategory]::ObjectNotFound, $Acc ) $PSCmdlet.ThrowTerminatingError($errorRecord) } } function DataIsEqual { param ( [object] $First, [object] $Second, [Microsoft.Win32.RegistryValueKind] $Type ) if ($Type -eq [Microsoft.Win32.RegistryValueKind]::String -or $Type -eq [Microsoft.Win32.RegistryValueKind]::ExpandString -or $Type -eq [Microsoft.Win32.RegistryValueKind]::DWord -or $Type -eq [Microsoft.Win32.RegistryValueKind]::QWord) { return @($First)[0] -ceq @($Second)[0] } # If we get here, $Type is either MultiString or Binary, both of which need to compare arrays. # The PolicyFileEditor module never returns type Unknown or None. $First = @($First) $Second = @($Second) if ($First.Count -ne $Second.Count) { return $false } $count = $First.Count for ($i = 0; $i -lt $count; $i++) { if ($First[$i] -cne $Second[$i]) { return $false } } return $true } function ParseKeyValueName { param ([string] $KeyValueName) if ($KeyValueName.EndsWith('\')) { $key = $KeyValueName -replace '\\$' $valueName = '' } else { $key = Split-Path $KeyValueName -Parent $valueName = Split-Path $KeyValueName -Leaf } return $key, $valueName } function GetTargetResourceCommon { param ( [string] $Path, [string] $KeyValueName ) $configuration = @{ PolicyType = $PolicyType KeyValueName = $KeyValueName Ensure = 'Absent' Data = $null Type = [Microsoft.Win32.RegistryValueKind]::Unknown } if (Test-Path -LiteralPath $path -PathType Leaf) { $key, $valueName = ParseKeyValueName $KeyValueName $entry = Get-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName if ($entry) { $configuration['Ensure'] = 'Present' $configuration['Type'] = $entry.Type $configuration['Data'] = $entry.Data } } return $configuration } function SetTargetResourceCommon { param ( [string] $Path, [string] $KeyValueName, [string] $Ensure, [string[]] $Data, [Microsoft.Win32.RegistryValueKind] $Type ) if ($null -eq $Data) { $Data = @() } try { Assert-ValidDataAndType -Data $Data -Type $Type } catch { Write-Error -ErrorRecord $_ return } $key, $valueName = ParseKeyValueName $KeyValueName if ($Ensure -eq 'Present') { Set-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName -Data $Data -Type $Type } else { Remove-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName } } function TestTargetResourceCommon { [OutputType([bool])] param ( [string] $Path, [string] $KeyValueName, [string] $Ensure, [string[]] $Data, [Microsoft.Win32.RegistryValueKind] $Type ) if ($null -eq $Data) { $Data = @() } try { Assert-ValidDataAndType -Data $Data -Type $Type } catch { Write-Error -ErrorRecord $_ return $false } $key, $valueName = ParseKeyValueName $KeyValueName $fileExists = Test-Path -LiteralPath $Path -PathType Leaf if ($Ensure -eq 'Present') { if (-not $fileExists) { return $false } $entry = Get-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName return $null -ne $entry -and $Type -eq $entry.Type -and (DataIsEqual $entry.Data $Data -Type $Type) } else # Ensure is 'Absent' { if (-not $fileExists) { return $true } $entry = Get-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName return $null -eq $entry } } function Assert-ValidDataAndType { param ( [string[]] $Data, [Microsoft.Win32.RegistryValueKind] $Type ) if ($Type -ne [Microsoft.Win32.RegistryValueKind]::MultiString -and $Type -ne [Microsoft.Win32.RegistryValueKind]::Binary -and $Data.Count -gt 1) { throw 'Do not pass arrays with multiple values to the -Data parameter when -Type is not set to either Binary or MultiString.' } } # SIG # Begin signature block # MIIhfgYJKoZIhvcNAQcCoIIhbzCCIWsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUfLft9jkOkXxurCsmGLRsFhFY # 4eOgghywMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0B # AQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg # Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg # +XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT # XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5 # a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g # 0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1 # roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf # GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G # A1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLL # gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3 # cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmr # EthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+ # fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5Q # Z7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu # 838fYxAe+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw # 8jCCBQswggPzoAMCAQICEAOiV15N2F/TLPzy+oVrWjMwDQYJKoZIhvcNAQEFBQAw # bzELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEuMCwGA1UEAxMlRGlnaUNlcnQgQXNzdXJlZCBJRCBD # b2RlIFNpZ25pbmcgQ0EtMTAeFw0xNDA1MDUwMDAwMDBaFw0xNTA1MTMxMjAwMDBa # MGExCzAJBgNVBAYTAkNBMQswCQYDVQQIEwJPTjERMA8GA1UEBxMIQnJhbXB0b24x # GDAWBgNVBAoTD0RhdmlkIExlZSBXeWF0dDEYMBYGA1UEAxMPRGF2aWQgTGVlIFd5 # YXR0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvcX51YAyViQE16mg # +IVQCQ0O8QC/wXBzTMPirnoGK9TThmxQIYgtcekZ5Xa/dWpW0xKKjaS6dRwYYXET # pzozoMWZbFDVrgKaqtuZNu9TD6rqK/QKf4iL/eikr0NIUL4CoSEQDeGLXDw7ntzZ # XKM86RuPw6MlDapfFQQFIMjsT7YaoqQNTOxhbiFoHVHqP7xL3JTS7TApa/RnNYyl # O7SQ7TSNsekiXGwUNxPqt6UGuOP0nyR+GtNiBcPfeUi+XaqjjBmpqgDbkEIMLDuf # fDO54VKvDLl8D2TxTFOcKZv61IcToOs+8z1sWTpMWI2MBuLhRR3A6iIhvilTYRBI # iX5FZQIDAQABo4IBrzCCAaswHwYDVR0jBBgwFoAUe2jOKarAF75JeuHlP9an90WP # NTIwHQYDVR0OBBYEFDS4+PmyUp+SmK2GR+NCMiLd+DpvMA4GA1UdDwEB/wQEAwIH # gDATBgNVHSUEDDAKBggrBgEFBQcDAzBtBgNVHR8EZjBkMDCgLqAshipodHRwOi8v # Y3JsMy5kaWdpY2VydC5jb20vYXNzdXJlZC1jcy1nMS5jcmwwMKAuoCyGKmh0dHA6 # Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9hc3N1cmVkLWNzLWcxLmNybDBCBgNVHSAEOzA5 # MDcGCWCGSAGG/WwDATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2Vy # dC5jb20vQ1BTMIGCBggrBgEFBQcBAQR2MHQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBMBggrBgEFBQcwAoZAaHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ29kZVNpZ25pbmdDQS0xLmNydDAM # BgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQBbzAp8wys0A5LcuENslW0E # oz7rc0A8h+XgjJWdJOFRohE1mZRFpdkVxM0SRqw7IzlSFtTMCsVVPNwU6O7y9rCY # x5agx3CJBkJVDR/Y7DcOQTmmHy1zpcrKAgTznZuKUQZLpoYz/bA+Uh+bvXB9woCA # IRbchos1oxC+7/gjuxBMKh4NM+9NIvWs6qpnH5JeBidQDQXp3flPkla+MKrPTL/T # /amgna5E+9WHWnXbMFCpZ5n1bI1OvgNVZlYC/JTa4fjPEk8d16jYVP4GlRz/QUYI # y6IAGc/z6xpkdtpXWVCbW0dCd5ybfUYTaeCJumGpS/HSJ7JcTZj694QDOKNvhfrm # MIIGajCCBVKgAwIBAgIQAwGaAjr/WLFr1tXq5hfwZjANBgkqhkiG9w0BAQUFADBi # MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 # d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENB # LTEwHhcNMTQxMDIyMDAwMDAwWhcNMjQxMDIyMDAwMDAwWjBHMQswCQYDVQQGEwJV # UzERMA8GA1UEChMIRGlnaUNlcnQxJTAjBgNVBAMTHERpZ2lDZXJ0IFRpbWVzdGFt # cCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjZF38 # fLPggjXg4PbGKuZJdTvMbuBTqZ8fZFnmfGt/a4ydVfiS457VWmNbAklQ2YPOb2bu # 3cuF6V+l+dSHdIhEOxnJ5fWRn8YUOawk6qhLLJGJzF4o9GS2ULf1ErNzlgpno75h # n67z/RJ4dQ6mWxT9RSOOhkRVfRiGBYxVh3lIRvfKDo2n3k5f4qi2LVkCYYhhchho # ubh87ubnNC8xd4EwH7s2AY3vJ+P3mvBMMWSN4+v6GYeofs/sjAw2W3rBerh4x8kG # LkYQyI3oBGDbvHN0+k7Y/qpA8bLOcEaD6dpAoVk62RUJV5lWMJPzyWHM0AjMa+xi # QpGsAsDvpPCJEY93AgMBAAGjggM1MIIDMTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0T # AQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCCAb8GA1UdIASCAbYwggGy # MIIBoQYJYIZIAYb9bAcBMIIBkjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln # aWNlcnQuY29tL0NQUzCCAWQGCCsGAQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1AHMA # ZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUAIABjAG8A # bgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAA # dABoAGUAIABEAGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAA # dABoAGUAIABSAGUAbAB5AGkAbgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0A # ZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBpAHQAIABsAGkAYQBiAGkAbABpAHQA # eQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUAZAAgAGgA # ZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgBhv1s # AxUwHwYDVR0jBBgwFoAUFQASKxOYspkH7R7for5XDStnAs0wHQYDVR0OBBYEFGFa # TSS2STKdSip5GoNL9B6Jwcp9MH0GA1UdHwR2MHQwOKA2oDSGMmh0dHA6Ly9jcmwz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMDigNqA0hjJo # dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNy # bDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj # ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcnQwDQYJKoZIhvcNAQEFBQADggEBAJ0l # fhszTbImgVybhs4jIA+Ah+WI//+x1GosMe06FxlxF82pG7xaFjkAneNshORaQPve # BgGMN/qbsZ0kfv4gpFetW7easGAm6mlXIV00Lx9xsIOUGQVrNZAQoHuXx/Y/5+IR # Qaa9YtnwJz04HShvOlIJ8OxwYtNiS7Dgc6aSwNOOMdgv420XEwbu5AO2FKvzj0On # cZ0h3RTKFV2SQdr5D4HRmXQNJsQOfxu19aDxxncGKBXp2JPlVRbwuwqrHNtcSCdm # yKOLChzlldquxC5ZoGHd2vNtomHpigtt7BIYvfdVVEADkitrwlHCCkivsNRu4PQU # Cjob4489yq9qjXvc2EQwggajMIIFi6ADAgECAhAPqEkGFdcAoL4hdv3F7G29MA0G # CSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0 # IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMTAyMTExMjAwMDBaFw0yNjAyMTAxMjAw # MDBaMG8xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV # BAsTEHd3dy5kaWdpY2VydC5jb20xLjAsBgNVBAMTJURpZ2lDZXJ0IEFzc3VyZWQg # SUQgQ29kZSBTaWduaW5nIENBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQCcfPmgjwrKiUtTmjzsGSJ/DMv3SETQPyJumk/6zt/G0ySR/6hSk+dy+PFG # hpTFqxf0eH/Ler6QJhx8Uy/lg+e7agUozKAXEUsYIPO3vfLcy7iGQEUfT/k5mNM7 # 629ppFwBLrFm6aa43Abero1i/kQngqkDw/7mJguTSXHlOG1O/oBcZ3e11W9mZJRr # u4hJaNjR9H4hwebFHsnglrgJlflLnq7MMb1qWkKnxAVHfWAr2aFdvftWk+8b/HL5 # 3z4y/d0qLDJG2l5jvNC4y0wQNfxQX6xDRHz+hERQtIwqPXQM9HqLckvgVrUTtmPp # P05JI+cGFvAlqwH4KEHmx9RkO12rAgMBAAGjggNDMIIDPzAOBgNVHQ8BAf8EBAMC # AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwggHDBgNVHSAEggG6MIIBtjCCAbIGCGCG # SAGG/WwDMIIBpDA6BggrBgEFBQcCARYuaHR0cDovL3d3dy5kaWdpY2VydC5jb20v # c3NsLWNwcy1yZXBvc2l0b3J5Lmh0bTCCAWQGCCsGAQUFBwICMIIBVh6CAVIAQQBu # AHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0 # AGUAIABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBl # ACAAbwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAg # AGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBnACAAUABhAHIAdAB5ACAAQQBn # AHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBpAHQAIABsAGkAYQBi # AGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0 # AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjAS # BgNVHRMBAf8ECDAGAQH/AgEAMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGB # BgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl # cnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2Vy # dC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMB0GA1UdDgQWBBR7aM4p # qsAXvkl64eU/1qf3RY81MjAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823I # DzANBgkqhkiG9w0BAQUFAAOCAQEAe3IdZP+IyDrBt+nnqcSHu9uUkteQWTP6K4fe # qFuAJT8Tj5uDG3xDxOaM3zk+wxXssNo7ISV7JMFyXbhHkYETRvqcP2pRON60Jcvw # q9/FKAFUeRBGJNE4DyahYZBNur0o5j/xxKqb9to1U0/J8j3TbNwj7aqgTWcJ8zqA # PTz7NkyQ53ak3fI6v1Y1L6JMZejg1NrRx8iRai0jTzc7GZQY1NWcEDzVsRwZ/4/I # a5ue+K6cmZZ40c2cURVbQiZyWo0KSiOSQOiG3iLCkzrUm2im3yl/Brk8Dr2fxIac # gkdCcTKGCZlyCXlLnXFp9UH/fzl3ZPGEjb6LHrJ9aKOlkLEM/zCCBs0wggW1oAMC # AQICEAb9+QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2 # MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNV # BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8G # A1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEF # AAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyB # n+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sM # NP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZw # iPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQ # rllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X # 8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYD # VR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYB # BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYK # YIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQu # Y29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFS # AEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBj # AGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBu # AGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQ # AFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAg # AEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABp # AGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwBy # AGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBl # AC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEE # bTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYB # BQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy # ZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0 # dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j # cmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXr # oq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgd # tlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1 # XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqp # FFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX # /l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7P # kdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8v # WpUIKRAmMYIEODCCBDQCAQEwgYMwbzELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp # Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEuMCwGA1UEAxMl # RGlnaUNlcnQgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EtMQIQA6JXXk3YX9Ms # /PL6hWtaMzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZ # BgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYB # BAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUJOIPTVUErGuPyPz19laT4mmn4M0wDQYJ # KoZIhvcNAQEBBQAEggEALsWEQQ1f8NorCMwwP42Fcyz5ksd8Dxtm8KTnXlLhaG2R # j0kA/Sm26VscjxOWTb8hTDuOKZsQCJ3vmlu4rXlj4ctCZ+OOwcffY8Em6pJyMhz+ # utYZybEWcKlj+BYFe7cDZuQs+P2MqrgznYl1aeI69JB9Y6vFuaeMKol5VGPvV6N6 # t0wiUxOta6BnG4UmZ7WFUj0mV20nHEfK2TnVE9u+/b/qlKTyRHC4/7JexGNSM1Yz # pM9aqQktI8uyN82Rm7FRYDkHio+XAOlKqR2yMM82EhchhJm9pqtS549S6S21MKMm # qa0ih66yrwB7BvnQhRQh7U4ccRjcKSeXom1+LYkDaKGCAg8wggILBgkqhkiG9w0B # CQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUA # oF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTUw # NDAxMDMyMTQ3WjAjBgkqhkiG9w0BCQQxFgQU865RMqqmk+VgTnTM8g7E4Jmkr8ow # DQYJKoZIhvcNAQEBBQAEggEAGauw3gBXJ48H/t8mfrW0VJ57QQxwa1BQ4JF0ZP6+ # Gs1hvOoR2vSECXxeRMzOqZloI9+gtnF6fMc6YkpEax/2q3t8z6o7CAGvfp8INc1q # Cs8YyI35G68AbSDUPSWWuwKKJ13aAS01AFtJhcNY1Q87edFXVsri4ki3wBORBmj1 # Q9HkmXSVqw28MBX5A4VNs6s7UKrPQ0QGMeIMrRjHDUSTx98mzBF/522k/CfGK2hd # 5fz9AbQMB5FNBTRMKaCyLkVAMuuWIrpo9Zuj7cyXL19uxLqP4C6ATjTcmMhjp2t8 # 1RiIWfBvaL/lsV4Fc70V/HCICWUic/X5e0uxJyn3r4bacQ== # SIG # End signature block |