Common.ps1
#requires -Version 2.0 $script:MachineExtensionGuids = '[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]' $script:UserExtensionGuids = '[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F73-3407-48AE-BA88-E8213C6761F1}]' 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 [TJX.PolFileEditor.FileFormatException] { $message = "File '$Path' is not a valid POL file." $exception = New-Object System.Exception($message) $errorRecord = New-Object System.Management.Automation.ErrorRecord( $exception, 'InvalidPolFileContents', [System.Management.Automation.ErrorCategory]::InvalidData, $Path ) throw $errorRecord } catch { $errorRecord = $_ $message = "Error loading policy file at path '$Path': $($errorRecord.Exception.Message)" $exception = New-Object System.Exception($message, $errorRecord.Exception) $newErrorRecord = New-Object System.Management.Automation.ErrorRecord( $exception, 'FailedToOpenPolicyFile', [System.Management.Automation.ErrorCategory]::OperationStopped, $Path ) throw $newErrorRecord } } 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) $newErrorRecord = New-Object System.Management.Automation.ErrorRecord( $exception, 'CreateParentFolderError', $errorRecord.CategoryInfo.Category, $Path ) throw $newErrorRecord } } 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) $newErrorRecord = New-Object System.Management.Automation.ErrorRecord( $exception, 'FailedToSavePolicyFile', [System.Management.Automation.ErrorCategory]::OperationStopped, $PolicyFile ) throw $newErrorRecord } } 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=$script:MachineExtensionGuids Version=$version gPCUserExtensionNames=$script:UserExtensionGuids "@ } 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=$script:MachineExtensionGuids" } if (-not $foundUserExtensionLine) { $foundUserExtensionLine = $true "gPCUserExtensionNames=$script:UserExtensionGuids" } } $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=$script:MachineExtensionGuids" } if (-not $foundUserExtensionLine) { $foundUserExtensionLine = $true "gPCUserExtensionNames=$script:MachineExtensionGuids" } } ) 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 pairs of GUIDs in "registry" format (with the curly braces), separated by nothing, with # each pair of GUIDs wrapped in square brackets. Example: # 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*(.*)$') { throw "Malformed gpt.ini line: $Line" } $valueName = $matches[1] $guidStrings = @($matches[2] -split '(?<=\])(?=\[)') if ($matches[1] -eq 'gPCMachineExtensionNames') { $toolExtensionGuid = $script:MachineExtensionGuids } else { $toolExtensionGuid = $script:UserExtensionGuids } $guidList = @( $guidStrings $toolExtensionGuid ) $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 ) throw $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 = @{ 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) { $errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'Do not pass arrays with multiple values to the -Data parameter when -Type is not set to either Binary or MultiString.' throw $errorRecord } } function InvalidDataTypeCombinationErrorRecord($Message) { $exception = New-Object System.Exception($Message) return New-Object System.Management.Automation.ErrorRecord( $exception, 'InvalidDataTypeCombination', [System.Management.Automation.ErrorCategory]::InvalidArgument, $null ) } # SIG # Begin signature block # MIIgQgYJKoZIhvcNAQcCoIIgMzCCIC8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBwJwFOwNQwAUL6 # rmiYVGlFFf0AkTutmiSN7DsyWKq+QaCCG0wwggO3MIICn6ADAgECAhAM5+DlF9hG # /o/lYPwb8DA5MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa # Fw0zMTExMTAwMDAwMDBaMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD # ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC # AQoCggEBAK0OFc7kQ4BcsYfzt2D5cRKlrtwmlIiq9M71IDkoWGAM+IDaqRWVMmE8 # tbEohIqK3J8KDIMXeo+QrIrneVNcMYQq9g+YMjZ2zN7dPKii72r7IfJSYd+fINcf # 4rHZ/hhk0hJbX/lYGDW8R82hNvlrf9SwOD7BG8OMM9nYLxj+KA+zp4PWw25EwGE1 # lhb+WZyLdm3X8aJLDSv/C3LanmDQjpA1xnhVhyChz+VtCshJfDGYM2wi6YfQMlqi # uhOCEe05F52ZOnKh5vqk2dUXMXWuhX0irj8BRob2KHnIsdrkVxfEfhwOsLSSplaz # vbKX7aqn8LfFqD+VFtD/oZbrCF8Yd08CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEXroq/0ksuCMS1Ri6enIZ3zbcgP # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQCiDrzf4u3w43JzemSUv/dyZtgy5EJ1Yq6H6/LV2d5Ws5/MzhQouQ2XYFwS # TFjk0z2DSUVYlzVpGqhH6lbGeasS2GeBhN9/CTyU5rgmLCC9PbMoifdf/yLil4Qf # 6WXvh+DfwWdJs13rsgkq6ybteL59PyvztyY1bV+JAbZJW58BBZurPSXBzLZ/wvFv # hsb6ZGjrgS2U60K3+owe3WLxvlBnt2y98/Efaww2BxZ/N3ypW2168RJGYIPXJwS+ # S86XvsNnKmgR34DnDDNmvxMNFG7zfx9jEB76jRslbWyPpbdhAbHSoyahEHGdreLD # +cOZUbcrBwjOLuZQsqf6CkUvovDyMIIFGjCCBAKgAwIBAgIQBGmOyDBQUQKKmQd8 # G6DyrjANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln # aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhE # aWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE1MDQw # MTAwMDAwMFoXDTE2MDYxNjEyMDAwMFowYTELMAkGA1UEBhMCQ0ExCzAJBgNVBAgT # Ak9OMREwDwYDVQQHEwhCcmFtcHRvbjEYMBYGA1UEChMPRGF2aWQgTGVlIFd5YXR0 # MRgwFgYDVQQDEw9EYXZpZCBMZWUgV3lhdHQwggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDEkWriT9tO2ZMDtjX9tCPoUBGkwuhUyLt45ab9WTX5OyO5Ym7j # +SMay+NSrfpkuudQKRPdu2SAimNNTFiNShrqHeLs5mdWAlA9DcKfcT/5ZIfGcwUH # Bd7iajpBdPrwi178PfTjX9dcCmECkksnXii1+gL4j0WG38bMHS5QnICthSzczV/i # Io/WRx9bDv7nYUMcHL83HYGLyHiaU9ia1vN0lLRqvM/1YMrDyipw6drgDJHmp0SN # mNq+4qXTZsvF2buVpCn77av17j038MM9/maCbz8KpxT6VoFdp+1NugeT+80I054X # AG0m1D1iDho7GQRSDsvzsk0z1SMOOgc5zk7tAgMBAAGjggG7MIIBtzAfBgNVHSME # GDAWgBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQUPup9rwK/lMYoH5Qb # 0iHlV/trvxQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcG # A1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v # c2hhMi1hc3N1cmVkLWNzLWcxLmNybDBCBgNVHSAEOzA5MDcGCWCGSAGG/WwDATAq # MCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGEBggr # BgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv # bTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAw # DQYJKoZIhvcNAQELBQADggEBAN8+txwY2kneaLV1H/dOX4z/61sdAo/2r5EgI8LA # xILnfOpsWg5zVuQQaLonOus+S/Mz5+QXpj2Y4gJ456jDj83Lm2fRWUBLDPRgJJV0 # eLbKcpmuvP/AZMHcyemDQXH9uiptROIB4Tbn72jW6brLq2cfJP3kGnRl9f/CdXCS # VJnTV+CJ3KpkeudaQWDXeIbxN3Kl+CTVk7Kwt9WV6mdJjUsXTG8GinXLoUOU1TWK # ujD34R3QWB6Hj0THRStZXIJiEllFxgi+pgfxlZs1zqAtjwF7HgqUEmUOYaYFrZou # DTJkbf6LuBnRqZprXuj4F04BxEcH5kOcYZJImmX4BdMiFcowggUwMIIEGKADAgEC # AhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEw # MjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV # BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEi # MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7 # RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p # 0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj # 6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grk # V7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHy # DxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMB # AAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAT # BgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG # GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh # Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCB # gQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNl # cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgG # CmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu # Y29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1 # DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQEL # BQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q # 3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/ # kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dc # IFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6 # dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT # +hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggZqMIIFUqADAgECAhADAZoCOv9YsWvW # 1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE # aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMT # GERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEw # MjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMG # A1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8 # a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTq # qEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWH # eUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8Ewx # ZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp # 2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMx # MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG # AQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsG # AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUH # AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy # AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj # AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg # AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ # AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt # AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj # AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl # AHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQft # Ht+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0f # BHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNz # dXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEF # BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRw # Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNy # dDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx # 7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVch # XTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBz # ppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1 # oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23s # Ehi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMC # 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 # WpUIKRAmMYIETDCCBEgCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp # Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMo # RGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQBGmOyDBQ # UQKKmQd8G6DyrjANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKAC # gAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsx # DjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCAz+1H8Q6U0xJxKd7r5eBT/ # Tfn2t61PnOrQTUjkoT50KzANBgkqhkiG9w0BAQEFAASCAQAbFHugpbakuYT7rjdj # F60gER4/vyt9mIn+HJQlStMrSOcGDXNLbj1BpFv1GurfQFJKUuQJtWSbC4RNLkUk # ngvaaqwJuU0z+J5edNM6PvbHhUSYpS2/lrwAIimHvM/z+qXZqS25MZpipLvBtN21 # Mz8A2bal2HB07Dw1JVwPWypnH5hGajtJeL3X71zBu4wzQHzZC5y71dTxhGe1ztp8 # 9vYCXEjshQm02wabP2RfC7YJ58joFZaeQE3+J6K3Z2q0hW1JUF/zuM9gRcTXc05m # QbOGRTlUcnmF/tAc9OfDcV92h7r14hhqwd6YwFvWbahS4XzdyrM5YhUxi6nD0ibn # 0PZWoYICDzCCAgsGCSqGSIb3DQEJBjGCAfwwggH4AgEBMHYwYjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xAhADAZoCOv9Y # sWvW1ermF/BmMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB # MBwGCSqGSIb3DQEJBTEPFw0xNjA1MDkwMzQ5MjhaMCMGCSqGSIb3DQEJBDEWBBQC # 1cE2yb41mvaAV9qB/qb6Rg3E7zANBgkqhkiG9w0BAQEFAASCAQBDRpnYNSj6dHTB # McJn1V9Ce6rdyHMYAZNEPvSkdAT9RtywxkqzxgrSMxa7adAuXOspEMINqyCUH575 # Fkxo5kgYEIYsAoDdhav9pCsSCfF8j+lARUYitYaFd+PkI1kEF+O6ML5tHG7mWXnT # ES/aRnU8G7SKUUarkSGMYIfh/ygDN1dURpFZHkwmtE61J4nH+QljcIg3CjWkpJA7 # Z3ebr+7NN5frEnln9tDrwt/r/0xr///wN1M0KgIvHs1oeIyq00I0s5zQinuPPUb1 # /mpPtT6fJlO3ogVEA1VwuxFqLPe/cInv6YOpGXt3B3kQS8oGyGot0vZuxrGrgNVK # 5P2CZxmm # SIG # End signature block |