Public/Get-ADFSTkHealth.ps1
function Get-ADFSTkHealth { [CmdletBinding()] param ( $ConfigFile, [ValidateSet("CriticalOnly", "Default", "Full")] $HealthCheckMode = "Default", [switch]$Silent ) $healtChecks = @{ CheckSignature = ($HealthCheckMode -ne "CriticalOnly") #Don't run i CriticalOnly CheckConfigVersion = $true MFAAccesControlPolicy = $true RemovedSPsStillInSPHash = ($HealthCheckMode -eq "Full") #Only run in Full mode MissingSPsInADFS = ($HealthCheckMode -eq "Full") #Only run in Full mode ScheduledTaskPresent = ($HealthCheckMode -eq "Full") #Checks if there are a Scheduled Task with the name 'Import Federated Metadata with ADFSToolkit' } enum Result { None Pass Warning Fail } $healthResults = @() #Get All paths if ([string]::IsNullOrEmpty($Global:ADFSTkPaths)) { $Global:ADFSTkPaths = Get-ADFSTKPaths } #region get config file(s) $configFiles = @() if ($PSBoundParameters.ContainsKey('configFile')) { $configFiles += $configFile } else { $configFiles = Get-ADFSTkConfiguration -ConfigFilesOnly | ? Enabled -eq $true | select -ExpandProperty ConfigFile } #endregion #region check script signatures if ($healtChecks.CheckSignature) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureStartMessage) $Signatures = Get-ChildItem -Path $Global:ADFSTkPaths.modulePath -Filter *.ps1 -Recurse | Get-AuthenticodeSignature $validSignatures = $Signatures | ? Status -eq Valid | Select -ExpandProperty Path $invalidSignatures = $Signatures | ? Status -eq HashMismatch | Select -ExpandProperty Path $missingSignatures = $Signatures | ? Status -eq NotSigned | Select -ExpandProperty Path Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureValidSignaturesResult -f $validSignatures.Count) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureInvalidSignaturesResult -f $invalidSignatures.Count) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureMissingSignaturesResult -f $missingSignatures.Count) #Signature(s) missing... if ($missingSignatures.Count -gt 0) { if ($Global:ADFSTkSkipNotSignedHealthCheck -eq $true) { $resultObject = [PSCustomObject]@{ CheckID = "CheckSignature" CheckName = "Signature check" ResultValue = [Result]::Pass ResultText = Get-ADFSTkLanguageText healthCheckSignatureSkipNotSignedMessage ResultData = @() ReferenceFile = "" FixID = "" } Write-ADFSTkVerboseLog $resultObject.ResultText $healthResults += $resultObject } else { $resultObject = [PSCustomObject]@{ CheckID = "CheckSignature" CheckName = "Signature check" ResultValue = [Result]::Fail ResultText = Get-ADFSTkLanguageText healthCheckSignatureMissingSignaturesResult -f $missingSignatures.Count ResultData = $missingSignatures ReferenceFile = "" FixID = "" } Write-ADFSTkLog (Get-ADFSTkLanguageText healthCheckSignatureMissingSignaturesMessage -f ($missingSignatures | Out-String)) -EntryType Warning $healthResults += $resultObject } } #Invalid signature(s)... if ($invalidSignatures.Count -gt 0) { $resultObject = [PSCustomObject]@{ CheckID = "CheckSignature" CheckName = Get-ADFSTkLanguageText healthCheckSignatureName ResultValue = [Result]::Fail ResultText = Get-ADFSTkLanguageText healthCheckSignatureInvalidSignaturesMessage -f ($invalidSignatures | Out-String) ResultData = $invalidSignatures ReferenceFile = "" FixID = "" } Write-ADFSTkVerboseLog $resultObject.ResultText -EntryType Warning $healthResults += $resultObject } if ($resultObject.ResultValue -eq [Result]::Pass) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignaturePass) } else { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureFail) } } #endregion #region check config version if ($healtChecks.CheckConfigVersion) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionStartMessage) foreach ($cf in $configFiles) { $resultObject = [PSCustomObject]@{ CheckID = "CheckConfigVersion" CheckName = "Version control" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = $cf FixID = "" } Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingPath -f $cf) if (Test-Path $cf) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingPathSucceeded) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingXMLParse) try { [xml]$xmlCf = Get-Content $cf Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingXMLParseSucceeded) #Check against compatible version Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionVerifyingVersionStart) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionVerifyingVersionCompareVersions -f $xmlCf.configuration.ConfigVersion, $Global:ADFSTkCompatibleInstitutionConfigVersion) if ([float]$xmlCf.configuration.ConfigVersion -ge [float]$Global:ADFSTkCompatibleInstitutionConfigVersion) { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthCheckConfigVersionVerifyingVersionSucceeded Write-ADFSTkVerboseLog $resultObject.ResultText } else { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText healthIncompatibleInstitutionConfigVersion -f $xmlCf.configuration.ConfigVersion, $Global:ADFSTkCompatibleInstitutionConfigVersion $resultObject.ResultData = $xmlCf.configuration.ConfigVersion Write-ADFSTkLog $resultObject.ResultText -EntryType Warning } } catch { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingXMLParseFailed -f $cf $resultObject.ResultData = $cf Write-ADFSTkLog $resultObject.ResultText -EntryType Warning } } else { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText cFileDontExist -f $cf $resultObject.ResultData = $cf Write-ADFSTkLog $resultObject.ResultText -EntryType Warning } $healthResults += $resultObject if ($resultObject.ResultValue -eq [Result]::Pass) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionPass) } else { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionFail) } } } #endregion #region check Access Control Policy if MFA Adapter is installed if ($healtChecks.MFAAccesControlPolicy) { $resultObject = [PSCustomObject]@{ CheckID = "MFAAccesControlPolicy" CheckName = "MFA Access Control Policy" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = "" FixID = "" } #Only if MFA Adapter installed! # Check if the ADFSTK MFA Adapter is installed and add rules if so if ([string]::IsNullOrEmpty($Global:ADFSTKRefedsMFAUsernamePasswordAdapterInstalled)) { $Global:ADFSTKRefedsMFAUsernamePasswordAdapterInstalled = ![string]::IsNullOrEmpty((Get-AdfsAuthenticationProvider -Name RefedsMFAUsernamePasswordAdapter -WarningAction Ignore)) } if ($Global:ADFSTKRefedsMFAUsernamePasswordAdapterInstalled) { if ((Get-AdfsAccessControlPolicy -Identifier ADFSToolkitPermitEveryoneAndRequireMFA) -eq $null) { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText healthMFAAccesControlPolicyInstalledACPMissing $resultObject.FixID = "CreateACP" } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthMFAAccesControlPolicyInstalledACPPresent } } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthMFAAccesControlPolicyNotInstalled } $healthResults += $resultObject if ($resultObject.ResultValue -eq [Result]::Pass) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthMFAAccesControlPolicyPass) } else { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthMFAAccesControlPolicyFail) } } #endregion #region check removedSPsStillInSPHash if ($healtChecks.removedSPsStillInSPHash) { #Automatically remove SP's from SPHash File that's not in the Metadata foreach ($cf in $configFiles) { $resultObject = [PSCustomObject]@{ CheckID = "RemovedSPsStillInSPHash" CheckName = "SP's in SPHash File not in Metadata" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = $cf FixID = "" } try { $instConfig = Get-ADFSTkInstitutionConfig -ConfigFile $cf $spHashFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.SPHashFile $metadataCacheFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.MetadataCacheFile if (Test-Path $spHashFile) { try { $fromHash = [string[]](Import-Clixml $spHashFile).Keys } catch { #What to do? #Rename it? Delete it? $resultObject.Checkname = "SPHash File corrupt" $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashSPHashCorrupt -f $spHashFile) $resultObject.ReferenceFile = $spHashFile $resultObject.FixID = "DeleteSPHashFile" } if ($resultObject.ResultValue -ne [Result]::Fail) { $MetadataXML = Get-ADFSTkMetadata -metadataURL $instConfig.configuration.metadataURL -CachedMetadataFile $metadataCacheFile $RawAllSPs = $MetadataXML.EntitiesDescriptor.EntityDescriptor | ? { $_.SPSSODescriptor -ne $null } $MetadataSPs = $RawAllSPs.EntityID $compare = Compare-ADFSTkObject $MetadataSPs $fromHash -CompareType InSecondSetOnly if ($compare.MembersInCompareSet -gt 0) { $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashMissingInMetadata -f $compare.MembersInCompareSet) $resultObject.ReferenceFile = $spHashFile $resultObject.ResultData = $compare.CompareSet $resultObject.FixID = "RemoveSPsFromSPHashFile" } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashNoSPsMissingInMetadata } } } else { $resultObject.CheckID = "SPHashMissing" $resultObject.CheckName = "SP Hash File existance" $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashAllSPsWillBeImported -f $spHashFile) $resultObject.ReferenceFile = $spHashFile } } catch { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = $_ } $healthResults += $resultObject } } #endregion #region remove/rerun missing SP's if ($healtChecks.MissingSPsInADFS) { #Automatically remove SP's from SPHash File that's not in the Metadata foreach ($cf in $configFiles) { $resultObject = [PSCustomObject]@{ CheckID = "MissingSPsInADFS" CheckName = "SP's in SPHash File missing in ADFS" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = $cf FixID = "" } try { $instConfig = Get-ADFSTkInstitutionConfig -ConfigFile $cf $spHashFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.SPHashFile $metadataCacheFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.MetadataCacheFile if (Test-Path $spHashFile) { try { $fromHash = [string[]](Import-Clixml $spHashFile).Keys } catch { #What to do? #Rename it? Delete it? $resultObject.Checkname = "SPHash File corrupt" $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashSPHashCorrupt -f $spHashFile) $resultObject.ReferenceFile = $spHashFile $resultObject.FixID = "DeleteSPHashFile" } if ($resultObject.ResultValue -ne [Result]::Fail) { $installed = [string[]](Get-ADFSTkToolEntityId -All | Select -ExpandProperty Identifier) $compare = Compare-ADFSTkObject $installed $fromHash -CompareType InSecondSetOnly if ($compare.MembersInCompareSet -gt 0) { $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthMissingSPsInADFSSPsMissingInADFS -f $compare.MembersInCompareSet) $resultObject.ResultData = $compare.CompareSet $resultObject.ReferenceFile = $spHashFile $resultObject.FixID = "AddMissingSPsInADFS" } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthMissingSPsInADFSNoSPsMissingInADFS } } } else { $resultObject.CheckID = "SPHashMissing" $resultObject.CheckName = "SP Hash File existance" $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashAllSPsWillBeImported -f $spHashFile) $resultObject.ReferenceFile = $spHashFile } } catch { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = $_ } $healthResults += $resultObject } } #endregion #region Check if Scheduled Task is present if ($healtChecks.ScheduledTaskPresent) { #Automatically remove SP's from SPHash File that's not in the Metadata $resultObject = [PSCustomObject]@{ CheckID = "ScheduledTaskPresent" CheckName = "Scheduled Task present" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = "" FixID = "" } $schedTask = Get-ScheduledTask -TaskName 'Import Federated Metadata with ADFSToolkit' -TaskPath "\ADFSToolkit\" -ErrorAction SilentlyContinue if (![string]::IsNullOrEmpty($schedTask)) { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthScheduledTaskPresentScheduledTaskPresent } else { $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = Get-ADFSTkLanguageText healthScheduledTaskPresentScheduledTaskNotPresent $resultObject.FixID = "RegisterScheduledTask" } $healthResults += $resultObject } #endregion #region Show result if (!$Silent) { $healthResults | Select CheckName, ResultValue, ResultText, ReferenceFile | sort ResultValue, CheckName, ReferenceFile | ft -AutoSize -Wrap } #endregion #region Correct fixable errors $FixedAnything = $false #region MFAAccesControlPolicy #createACP $MFAAccesControlPolicy = $healthResults | ? FixID -eq "CreateACP" if (![String]::IsNullOrEmpty($MFAAccesControlPolicy)) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthMFAAccesControlPolicyRegisterACP))) { New-ADFSTKAccessControlPolicy Write-ADFSTkLog "Access Control Policy 'ADFSTk:Permit everyone and force MFA' created." $MFAAccesControlPolicy.ResultValue = [Result]::Pass $MFAAccesControlPolicy.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $FixedAnything = $true } } #endregion #region RemovedSPsStillInSPHash #Do we have SP's in SP Hash file that are missing in the Metadata? They should be removed... $removedSPsStillInSPHash = $healthResults | ? FixID -eq "RemoveSPsFromSPHashFile" if (![String]::IsNullOrEmpty($removedSPsStillInSPHash)) { foreach ($resultObject in $removedSPsStillInSPHash) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthRemoveSPsFromSPHashFileRemoveSPsNotInMetadata -f $resultObject.ReferenceFile ))) { Remove-ADFSTkEntityHash -SPHashFile $resultObject.ReferenceFile -EntityIDs $resultObject.ResultData $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } else { #No need to fail, carry on! } } } #Do we have corrupt SP Hash files? $removedSPsStillInSPHash = $healthResults | ? FixID -eq 'DeleteSPHashFile' if (![String]::IsNullOrEmpty($removedSPsStillInSPHash)) { foreach ($resultObject in $removedSPsStillInSPHash) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthDeleteSPHashFileDeleteCorruptSPHashFile -f $resultObject.ReferenceFile ))) { Remove-Item -Path $resultObject.ReferenceFile $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } else { #The fail result will stand! } } } #endregion #region RemovedSPsStillInSPHash #Do we have SP's in SP Hash file that are missing in ADFS? They should be removed from SP Hash File... $addMissingSPs = $healthResults | ? FixID -eq "AddMissingSPsInADFS" if (![String]::IsNullOrEmpty($addMissingSPs)) { foreach ($resultObject in $addMissingSPs) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthAddMissingSPsInADFSRemoveMissingSPs -f $resultObject.ReferenceFile ))) { Remove-ADFSTkEntityHash -SPHashFile $resultObject.ReferenceFile -EntityIDs $resultObject.ResultData $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } else { #No need to fail, carry on! } } } #endregion #region Add Scheduled Task #Only if run manually if (!$Silent) { $addMissingSPs = $healthResults | ? FixID -eq "RegisterScheduledTask" if (![String]::IsNullOrEmpty($addMissingSPs)) { if ((Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthRegisterScheduledTaskCreateSchedTask))) { Register-ADFSTkScheduledTask $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } } } #endregion if ($FixedAnything -and -not $Silent) { $healthResults | Select CheckName, ResultValue, ResultText, ReferenceFile | sort ResultValue, CheckName, ReferenceFile | ft -AutoSize -Wrap } if ($Silent) { return !($healthResults.ResultValue.Contains([Result]::Fail)) } else { if ($healthResults.ResultValue.Contains([Result]::Fail)) { Write-ADFSTkLog -Message (Get-ADFSTkLanguageText healthFailed) -EntryType Error } elseif ($healthResults.ResultValue.Contains([Result]::Warning)) { Write-ADFSTkLog -Message (Get-ADFSTkLanguageText healthPassedWithWarnings) -EntryType Warning } else { Write-ADFSTkLog -Message (Get-ADFSTkLanguageText healthPassed) -EntryType Information -ForegroundColor Green } } } # SIG # Begin signature block # MIId/gYJKoZIhvcNAQcCoIId7zCCHesCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDLcfA3fPJXETiY # 6WJc9R29OKM7Rocvky024/XH3TvleKCCGKwwggR9MIIDZaADAgECAgMb5xUwDQYJ # KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRk # eSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZp # Y2F0aW9uIEF1dGhvcml0eTAeFw0xNDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBa # MIGDMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv # dHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMTAvBgNVBAMTKEdv # IERhZGR5IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/cWII8fpZNPcbyRij94BJWOkigxOmxSBD # ATuE8eaFSZ8n6vaEG06gtNtwmMcyAbEFPgdO7vT6Ty9ZMCLnqxlWa+KAB/zzFnWA # OVF75fk1tnROqY2CE+S2P6kDg/qivooVan/eC8O2GRQFyurDqASUO0Z8Mg3zAGYi # yI1pbTaMERi307IcYLQ4+gKMztPdRgfeCj7rXXzIfPuwK1OkkmJpUSUFYRpEgYws # qUOWI9+sOoGaDinFHKnpXR62np4wCjnO8YiA+0tdzDLshWJDJTQCVicBkbQ7cCo/ # brHonIgBfZ/U+dtTbWCdvyznWKu4X0b8zsQbAzwJ60kxXGlGs+BHAgMBAAGjggEX # MIIBEzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU # OpqFBxBnKLbv9r0FQW4gwZTaD94wHwYDVR0jBBgwFoAU0sSw0pHUTBFxs2HLPaH+ # 3ahq1OMwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5n # b2RhZGR5LmNvbS8wMgYDVR0fBCswKTAnoCWgI4YhaHR0cDovL2NybC5nb2RhZGR5 # LmNvbS9nZHJvb3QuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIB # FiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3 # DQEBCwUAA4IBAQBZC1O9koYRpyR77Vsxzx0fbHDFuG6+Trv2vpdQ4TB/uihcYpTC # 434z9/tCdoXblRyMIlh1CQyIZWc5ChYJxaA4l6TFI5M/tBimAQZEkeOnaSe0WiV/ # Orcyzd2E/yo4KTOk3Weyhf6hiCAcUInI3Cr2QgM3TOaI39WvJPKxw9/MtezgmV63 # SVQgPJQYDMccUhhJpG3hs1gLydjs2a4cMo4ocA3i/qYXnoQPvVdws1rpH6CGU7vv # fP9pC+BIw7eTC8gKVMSsXRRnN2zKpS8xCDeqbm+MvJviV10kga+Xl5yErWysN0xm # 82GRESDkvjCfeqQpCbDhNF9kdxhAUd+MMKavMIIE0DCCA7igAwIBAgIBBzANBgkq # hkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzAR # BgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTEw # LwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy # MB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3MDAwMFowgbQxCzAJBgNVBAYTAlVT # MRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQK # ExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFk # ZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2Vy # dGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw # ggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzDBNliF44v/z5lz4/OYuY8UhzaFkVL # Vat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOvK/6AYZ15V8TPLvQ/MDxdR/yaFrzD # N5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23ecSZHjzhHU9FGHbTj3ADqRay9vHHZ # qm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HYpDNO6rPWJ0+tJYqlxvTV0KaudAVk # V4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7neTOvDCAHf+jfBDnCaQJsY1L6d8Eb # yHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMBAAGjggEaMIIBFjAPBgNVHRMBAf8E # BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUQMK9J47MNIMwojPX+2yz # 8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv9r0FQW4gwZTaD94wNAYIKwYBBQUH # AQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wNQYD # VR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZHJvb3QtZzIu # Y3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIBFiVodHRwczovL2Nl # cnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQAI # fmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz91cxG7685C/b+LrTW+C05+Z5Yg4M # otdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2RJ17LJ3lXubvDGGqv+QqG+6EnriD # fcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawiDsoXiWJYRBuriSUBAA/NxBti21G0 # 0w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11GIo/ikGQI31bS/6kA1ibRrLDYGCD # +H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2xLXY2JtwE65/3YR8V3Idv7kaWKK2h # Jn0KCacuBKONvPi8BDABMIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TAN # BgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg # SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2Vy # dCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAw # MFoXDTMxMDEwNjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD # ZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFC # vQp1dU2UtAxQtSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in4 # 3Stwhd4CGPN4bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6 # t2HWc+xObTOKfF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0 # TXhG4wODMSlKXAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK0 # 2/xWVLwfoYervnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEA # AaOCAbgwggG0MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB # /wQMMAoGCCsGAQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYB # BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0 # tuEgHf4prtLkYaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNq # erwwcQYDVR0fBGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3No # YTItYXNzdXJlZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv # bS9zaGEyLWFzc3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUH # MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDov # L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVz # dGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5n # Rv1CclF0CiNHo6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2 # uHDPYuj1UUp4eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmD # gvoaU+2QzI2hF3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QR # cucbZEnYIpp1FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4b # TxMd90oNcX6Xt/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aK # LzCCBRwwggQEoAMCAQICCGXB0JJJvDvXMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD # VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa # MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0 # cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj # dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTIxMDExOTE4MzczNloX # DTIyMDMwODE4NTgwMFowXjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x # DzANBgNVBAcTBk90dGF3YTEVMBMGA1UEChMMQ0FOQVJJRSBJbmMuMRUwEwYDVQQD # EwxDQU5BUklFIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ # hfCjFqiTmN1uLoySixnwaOjf/ZAL9P6SvjlCaBA2mutoorEgnzUP8HnOIcvMRgEM # PmpaZ8egM93Bmx9d41xoarsQpCN3DhYOo+b3fWnPucVtpxbul2OFePv63mw/uvr+ # dqkv4b/f3Tg+ilQbpsNonbvh9MKEFv8Pn9koj0ySV+qxz34PxTVAe6g//pel3/3i # 9fqilCnIEcx4zg/+NKBeOWROSs4oXo3IvBjVrunmz+YuieSr78TqIE6hD8JF2q1w # KwfMB3+x7dEXZAus9WtIU/qITATtEfO9QAgrrYL4F1MLN+osSp8my5eCOjnLTQc4 # 7q574V3zQhsIHW7yBXLdAgMBAAGjggGFMIIBgTAMBgNVHRMBAf8EAjAAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDA1BgNVHR8ELjAsMCqgKKAm # hiRodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkaWcyczUtNi5jcmwwXQYDVR0gBFYw # VDBIBgtghkgBhv1tAQcXAjA5MDcGCCsGAQUFBwIBFitodHRwOi8vY2VydGlmaWNh # dGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMAgGBmeBDAEEATB2BggrBgEFBQcB # AQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzBABggr # BgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0 # b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRAwr0njsw0gzCiM9f7bLPwtCyAzjAd # BgNVHQ4EFgQUUPnMg2nmYS8l7rmax3weVkrgz5AwDQYJKoZIhvcNAQELBQADggEB # AGabJLu09gdYHt7ZMbpJ4048ZIiXwVLE/HNcnApTghNaHnSSiMI2xTsmbrM/lYsm # pwFuws1c2fMBvyDRgkzR/4+RIjoQJpLrHy1QABYlWAIKMqdFmfqty0QApgIkGN2+ # scMxKMWJGND8qp3KM+5C8TNTsO0gPVfdaarX2TmLM6yIQcgxD8YZMd0mqdR7rcCe # bgMeAdHLYPQu/HM0Cj3qtzFx/CZzz93CAlh8Dx5woqeNJixQMLK28MhU8y6NSN5o # KnD/8EESudRzXyoowZ2N4YJzyye5UL9pxhniDKs444w1r5XcjQYDo11G8Y4up4XW # 1cFtLNulHYcKhAnQ7XHswxMwggUxMIIEGaADAgECAhAKoSXW1jIbfkHkBdo2l8IV # MA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD # ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAxMDcx # MjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX # BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIg # QXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1ofue2 # oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb6+NGRwYa # VX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJcWvg # zyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mIUF79Zm5W # YScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfxFwbvPc3W # Te8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAdBgNVHQ4E # FgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGL # p6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYD # VR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNl # cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEG # A1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkwRzA4Bgpg # hkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv # bS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdWac3v # 3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z+Cei # Zr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC4HLHmNY8 # ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+tpJn+1Nh # iaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6HUSHkWGCb # ugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjayS6JK # ldj1po5SMYIEqDCCBKQCAQEwgcEwgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdB # cml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNv # bSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9z # aXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0 # aG9yaXR5IC0gRzICCGXB0JJJvDvXMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQB # gjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIK2D/RTs # MiZCGHi+5hkKLcbJLBT+bJvt7DXJ5WDCVl+AMA0GCSqGSIb3DQEBAQUABIIBAGyX # ptHUbdJBtJ7nyipKZwIFzEHgk8KZk3nlc2dn0+uHxgmpf0+H/qf/bfhtq4fLUZGk # Y6GRZbkAAK84J5zwPWMSvDd534H6trZRbBrxQRcstZ/q84j46BsAiFlH9OhxG7Rz # vKfJLCHy6SghtCod2kKAq7aYJeTwsK9qKGvox9YpHqSTtLbKjexq8JQYHxKUwvVa # 5J0JkbBuzbvccEM1PDdwH8BoY5GUT9pnixMFDYKnDzXgY/yymjUhNGbsYl52hIZH # FB/d4T+d60rEi3zhCcGgIpVnij3nIyRcibdoX6wvRTK7s6PeDKu7ekIsKXqBb2Sh # 0IBQrC5NKlZcm+/AbU6hggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYw # cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk # IElEIFRpbWVzdGFtcGluZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQME # AgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X # DTIxMTExNTIwMDY0OFowLwYJKoZIhvcNAQkEMSIEIDZVLGzoYRYgzX7TzWxMKmXr # fuJeiYr8+9Xi17IsKAOCMA0GCSqGSIb3DQEBAQUABIIBAMJCg8qHlQw2l516wQtD # oHtN4JLw5k3CpJwJbUpSAw+NL36wKMVakl2K177iuzPob6r28PEMtS1VgeoFoY2+ # jRKi1v3G9WcYCTMIjOSXVQ5bJCWmvv1CVS0lMtIMlEOq1a9KVQHWEupSZX5RJWWm # ywRyCIdRjabPlD5beUrNqG8d1oBCOF9wwhFIx+GS/D2RyNr2lcRerC3r/ULjj7e5 # az5nimvVmRdeRXQWEH4H95vxwacFmuvCQORgUlqmMOfpjLcrPQVNmnjkEH8cHQdx # PjNZG7SUAVbWVxw9MQ9FHU+UIRpc5CdwZW+PwIvPhvtUJuWQyncla8mJ1ummTp1F # UEs= # SIG # End signature block |