From 4e2dfa421effbca4a2222287c22915b282e99be4 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 8 Jul 2022 12:23:54 +0200 Subject: [PATCH 01/11] Add Kerberos loopback test --- NuGet.config | 10 + .../Security/Kerberos/FakeKdcCertificate.cs | 289 ++++++++++++++++++ .../Net/Security/Kerberos/FakeKdcServer.cs | 88 ++++++ .../Kerberos/FakeKerberosPrincipal.cs | 180 +++++++++++ .../Security/Kerberos/FakePrincipalService.cs | 83 +++++ .../Security/Kerberos/FakeRealmReferral.cs | 27 ++ .../Net/Security/Kerberos/FakeRealmService.cs | 31 ++ .../Security/Kerberos/FakeRealmSettings.cs | 24 ++ .../Security/Kerberos/FakeTrustedRealms.cs | 28 ++ .../Net/Security/Kerberos/KerberosExecutor.cs | 115 +++++++ ...tem.Net.Security.Kerberos.Shared.projitems | 44 +++ .../NegotiateAuthenticationKerberosTest.cs | 62 ++++ .../System.Net.Security.Tests.csproj | 3 + 13 files changed, 984 insertions(+) create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems create mode 100644 src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs diff --git a/NuGet.config b/NuGet.config index a6d878497eacd0..d46359cd51223b 100644 --- a/NuGet.config +++ b/NuGet.config @@ -21,7 +21,17 @@ + + + + + + + + + + diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs new file mode 100644 index 00000000000000..9bb69ffac24b5b --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs @@ -0,0 +1,289 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security.Cryptography.X509Certificates; + +namespace System.Net.Security.Kerberos; + +class FakeKdcCertificate +{ + static ReadOnlySpan kdcPfxData => new byte[] { + 0x30, 0x82, 0x11, 0x2d, 0x02, 0x01, 0x03, 0x30, 0x82, 0x10, 0xe9, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, 0x10, 0xda, 0x04, 0x82, 0x10, 0xd6, 0x30, 0x82, + 0x10, 0xd2, 0x30, 0x82, 0x06, 0x4b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01, 0xa0, 0x82, 0x06, 0x3c, 0x04, 0x82, 0x06, 0x38, 0x30, 0x82, 0x06, 0x34, 0x30, 0x82, 0x06, + 0x30, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, + 0x04, 0xfe, 0x30, 0x82, 0x04, 0xfa, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04, 0x08, 0x3a, 0x2a, 0x8d, 0x7f, 0x36, 0x9e, 0xfd, 0xaa, + 0x02, 0x02, 0x07, 0xd0, 0x04, 0x82, 0x04, 0xd8, 0xb8, 0x64, 0x7a, 0x53, 0xcf, 0x06, 0x10, 0xc8, + 0xdd, 0xab, 0x43, 0xdd, 0x82, 0x8a, 0x56, 0xe7, 0xee, 0x54, 0x3a, 0x43, 0x4e, 0xe7, 0x63, 0xd5, + 0xc5, 0x49, 0xd4, 0x59, 0xfa, 0x54, 0x27, 0x38, 0xf2, 0x8c, 0xc5, 0xe9, 0xf1, 0x20, 0xe2, 0x32, + 0xcb, 0x64, 0xe4, 0xd8, 0xb2, 0xe1, 0x72, 0x79, 0xc9, 0xb5, 0x2f, 0x5a, 0x9b, 0xb4, 0x09, 0x20, + 0x4f, 0x9d, 0x5c, 0x26, 0x48, 0xc6, 0x89, 0xb9, 0x87, 0xc0, 0xda, 0xb8, 0x5b, 0x18, 0x77, 0x2b, + 0x35, 0x95, 0x89, 0x36, 0x19, 0x36, 0xe6, 0xb5, 0x00, 0x52, 0x3d, 0x2b, 0x40, 0x59, 0x1d, 0xb1, + 0xa1, 0x59, 0x8e, 0x13, 0xe1, 0x39, 0x6e, 0xd5, 0xa9, 0xc8, 0xfd, 0x8b, 0x41, 0xeb, 0x44, 0xca, + 0x08, 0xa1, 0x1a, 0xf5, 0xce, 0x66, 0xf6, 0x7f, 0xba, 0xf3, 0x73, 0x58, 0xf6, 0x58, 0x10, 0x45, + 0x5e, 0xaa, 0x86, 0x7f, 0x2c, 0x88, 0x39, 0xab, 0x87, 0x95, 0xd1, 0x0e, 0x7b, 0x60, 0xa8, 0x90, + 0x2b, 0xa6, 0xb9, 0xeb, 0x1c, 0x15, 0x13, 0xf7, 0xef, 0xac, 0x88, 0x0f, 0x7b, 0x8f, 0xbb, 0xcd, + 0x4b, 0x22, 0x87, 0xea, 0xe7, 0x81, 0xa8, 0x06, 0x74, 0x0a, 0xd5, 0x7f, 0xe1, 0x5b, 0x77, 0x81, + 0x9d, 0x71, 0xeb, 0x21, 0x5d, 0x25, 0x1d, 0x05, 0x19, 0x4b, 0x93, 0x79, 0x94, 0x24, 0x5e, 0xa7, + 0xe2, 0x85, 0x3d, 0xf7, 0xdc, 0xf4, 0xec, 0x0e, 0x89, 0x7c, 0xb9, 0xbc, 0xfc, 0xc2, 0xdf, 0xac, + 0x8d, 0x86, 0xe8, 0xe2, 0x0a, 0xc8, 0x3f, 0xa8, 0x45, 0xa1, 0x87, 0x15, 0xe4, 0x20, 0x5e, 0xff, + 0xf1, 0x11, 0x51, 0x23, 0xf1, 0x08, 0x2b, 0x4f, 0x80, 0x98, 0xeb, 0xc7, 0xe5, 0x92, 0xeb, 0xa3, + 0xd2, 0xeb, 0x1f, 0x18, 0x23, 0x7a, 0x5c, 0x0e, 0x26, 0xc1, 0x90, 0xcc, 0x95, 0x1b, 0xea, 0x69, + 0x9e, 0x15, 0x6c, 0xef, 0xc8, 0x21, 0xb7, 0x04, 0x89, 0x49, 0x2b, 0x6e, 0xa6, 0xf6, 0x67, 0x7c, + 0x75, 0xb2, 0x98, 0x77, 0xdb, 0x0c, 0x28, 0xaa, 0x5d, 0x46, 0x4c, 0x3c, 0x18, 0x6a, 0x87, 0x9d, + 0xf8, 0xee, 0x24, 0x52, 0xe2, 0xd2, 0xc9, 0x4e, 0x98, 0x39, 0x4c, 0xe6, 0x95, 0x12, 0x4c, 0x9b, + 0x96, 0x0f, 0xb5, 0xb7, 0xb0, 0x28, 0x13, 0x3c, 0xf7, 0xb9, 0xa2, 0x8a, 0xc8, 0xa9, 0x8d, 0xe8, + 0x4f, 0x96, 0x80, 0xf5, 0x2e, 0x78, 0xe9, 0x62, 0xf7, 0x9b, 0xbb, 0xb0, 0x4c, 0x5c, 0xd0, 0xcf, + 0x51, 0xcb, 0xac, 0x1a, 0xe6, 0x4f, 0x33, 0x54, 0x00, 0xb7, 0xd2, 0x05, 0xc2, 0x1d, 0xe9, 0xf0, + 0x3e, 0x85, 0x7c, 0x28, 0x97, 0xdb, 0xe8, 0xaa, 0x6d, 0xc2, 0xb5, 0x6e, 0xf1, 0x54, 0x30, 0xc0, + 0x53, 0x56, 0x9b, 0x1a, 0xb2, 0x6e, 0xb9, 0x9a, 0xc7, 0xed, 0xbc, 0x57, 0x07, 0x27, 0x2a, 0x44, + 0x18, 0x6d, 0xb8, 0x81, 0x80, 0xcf, 0x6e, 0xe3, 0x8f, 0x23, 0x4b, 0xcc, 0x7d, 0xdd, 0x22, 0x5a, + 0x90, 0x77, 0xc0, 0xc9, 0xd4, 0xad, 0x94, 0xf1, 0xa4, 0x41, 0x5f, 0x50, 0xf1, 0x0c, 0x8d, 0xa8, + 0xe3, 0xc5, 0x0a, 0x3f, 0x9b, 0xf2, 0x06, 0xa0, 0xdb, 0xef, 0x19, 0x72, 0x3f, 0x8d, 0x6e, 0xf8, + 0x90, 0xd8, 0x6f, 0x24, 0x42, 0xc3, 0xf4, 0x34, 0xe5, 0x4b, 0xe1, 0xa9, 0x5d, 0xe3, 0xbc, 0x57, + 0x13, 0x14, 0x2e, 0x72, 0xb5, 0x7c, 0xf2, 0x34, 0x79, 0x2c, 0x9b, 0x95, 0x0b, 0xd6, 0x33, 0x1a, + 0xbe, 0x7d, 0xf9, 0x0f, 0x6e, 0xe8, 0x9a, 0x37, 0x0f, 0x8f, 0xde, 0x0f, 0xb0, 0x30, 0xbe, 0xe8, + 0x20, 0x1c, 0xb9, 0xcd, 0xd6, 0x57, 0xbc, 0x30, 0xb2, 0x8e, 0x25, 0xec, 0x5f, 0xb0, 0x64, 0xc7, + 0x7d, 0x0e, 0xe2, 0xb8, 0x52, 0xb5, 0xa8, 0x37, 0x94, 0xb3, 0x8c, 0x9c, 0x18, 0x3d, 0xb7, 0xca, + 0x37, 0x16, 0xac, 0xca, 0x7b, 0xee, 0xe7, 0x14, 0xb1, 0x8e, 0x28, 0x57, 0x05, 0xd3, 0x30, 0xcd, + 0x98, 0xe8, 0xdd, 0xc5, 0x64, 0xa5, 0x36, 0x86, 0x8d, 0x04, 0xc2, 0x79, 0x4a, 0x8b, 0x4a, 0x36, + 0x67, 0x75, 0x48, 0x6a, 0x41, 0x7b, 0x5e, 0x36, 0xeb, 0x4f, 0xd2, 0xe7, 0xb5, 0x41, 0x7b, 0xbd, + 0x5a, 0xfb, 0xe8, 0x64, 0xfb, 0x0a, 0x6a, 0x20, 0x9e, 0x5f, 0x3d, 0x9e, 0x98, 0x57, 0xd0, 0xea, + 0x7a, 0xee, 0xa0, 0x2b, 0x89, 0x2e, 0xee, 0x5b, 0xf6, 0xd3, 0x31, 0xa3, 0x59, 0xda, 0x25, 0xbf, + 0x9f, 0xcf, 0xd5, 0x69, 0xba, 0xc8, 0xf6, 0x7f, 0x41, 0x3a, 0x2d, 0xc1, 0x34, 0xdf, 0x7e, 0x0c, + 0x24, 0xec, 0x76, 0x8d, 0x2d, 0xc7, 0x40, 0xb3, 0xa4, 0xe3, 0x00, 0x92, 0xd8, 0x8d, 0xf0, 0x27, + 0xa6, 0xee, 0x17, 0x3e, 0x30, 0xb9, 0x05, 0x4f, 0x0e, 0x9e, 0xb4, 0x47, 0xe9, 0xb5, 0xe5, 0xfe, + 0xad, 0xae, 0xf1, 0xc4, 0xaa, 0xdd, 0x46, 0x74, 0x41, 0xc6, 0x37, 0xf5, 0xe3, 0xd3, 0xd7, 0x2b, + 0x45, 0x9a, 0x24, 0x5e, 0xc2, 0xde, 0x77, 0xdf, 0x17, 0xc8, 0xd5, 0x62, 0xa0, 0xb7, 0x70, 0xbf, + 0x53, 0x55, 0xf0, 0xfa, 0xaa, 0x9b, 0x64, 0x8e, 0xcc, 0x9c, 0xf9, 0xa8, 0x05, 0x9e, 0xbd, 0x07, + 0xe6, 0x1d, 0xbe, 0x57, 0x4f, 0xeb, 0x38, 0xa2, 0x44, 0x87, 0x95, 0x0c, 0x26, 0xe5, 0x43, 0x2a, + 0x02, 0xc3, 0x32, 0xdb, 0x61, 0x36, 0xa9, 0x06, 0xd9, 0x75, 0x9b, 0x50, 0x7f, 0x31, 0xb8, 0xc4, + 0x6c, 0x1c, 0x76, 0x94, 0xae, 0xf8, 0xdc, 0x50, 0x62, 0x51, 0x07, 0xba, 0xe0, 0xac, 0x58, 0xc5, + 0x50, 0xd4, 0xd9, 0xbd, 0x4d, 0xcc, 0x2f, 0x2e, 0xdb, 0x7c, 0x8b, 0xa1, 0xb5, 0xc8, 0x76, 0x29, + 0xb9, 0x8b, 0xba, 0xb5, 0x37, 0xea, 0xb6, 0xa7, 0x4c, 0x5b, 0x27, 0x3d, 0x1e, 0x2f, 0xf8, 0x2e, + 0x3e, 0x49, 0xf0, 0x17, 0xc3, 0x08, 0xd3, 0xe7, 0x28, 0x84, 0x7c, 0xee, 0x84, 0xd9, 0x71, 0x4a, + 0x17, 0x3c, 0xfd, 0x0f, 0x80, 0xf5, 0xcd, 0xe4, 0x53, 0xcd, 0x38, 0xe7, 0x26, 0x3a, 0x39, 0xb4, + 0x14, 0x9b, 0x23, 0xfe, 0x12, 0x72, 0x99, 0xe2, 0xe8, 0xd9, 0x06, 0x26, 0x35, 0x2d, 0xc9, 0x0e, + 0x3c, 0x41, 0x0f, 0x24, 0x1c, 0xdb, 0x0d, 0x05, 0xdb, 0xe3, 0x05, 0x4a, 0x8c, 0x8d, 0xe1, 0x8d, + 0xbd, 0x71, 0x0a, 0xf9, 0xf1, 0x47, 0x8b, 0x62, 0x65, 0x50, 0x9d, 0xe2, 0x41, 0x57, 0x7f, 0x02, + 0x3a, 0x26, 0xc7, 0xd2, 0xd6, 0xdd, 0xdc, 0xa9, 0xc5, 0x8b, 0xb0, 0x0e, 0x56, 0x14, 0x44, 0x2a, + 0x19, 0x01, 0x99, 0xba, 0x0c, 0xdf, 0x4e, 0x06, 0xf2, 0x34, 0x7c, 0x33, 0x32, 0xed, 0xe0, 0x59, + 0xd8, 0x3e, 0x0a, 0x46, 0xac, 0x05, 0x75, 0xc7, 0x57, 0xf1, 0x25, 0xa9, 0x9b, 0xd8, 0xdb, 0x98, + 0xee, 0xe0, 0xd3, 0xcb, 0xda, 0xd2, 0x1a, 0x68, 0x4d, 0xa2, 0x20, 0x12, 0x86, 0x0c, 0x11, 0xdb, + 0x0c, 0x79, 0xe6, 0xdd, 0xd5, 0xb6, 0x60, 0xe7, 0xfa, 0xca, 0xd0, 0x2c, 0xb2, 0x7d, 0xf9, 0x9f, + 0xf4, 0x95, 0xfe, 0xcd, 0xb1, 0xa5, 0x89, 0xfe, 0xfc, 0x48, 0x15, 0xe0, 0x52, 0x3f, 0x01, 0x72, + 0xfb, 0x05, 0x65, 0x69, 0x8d, 0x00, 0x39, 0x5c, 0x25, 0xea, 0xb7, 0x63, 0xa9, 0xb9, 0x64, 0x13, + 0x4e, 0x75, 0x43, 0xaf, 0x60, 0x97, 0x2c, 0xf3, 0x0c, 0x5a, 0x5f, 0xc9, 0x05, 0x01, 0xfe, 0x51, + 0xfb, 0x10, 0xa6, 0x8a, 0x6d, 0xab, 0x57, 0x43, 0x53, 0x5e, 0xc2, 0x6d, 0xfb, 0x37, 0xce, 0xe8, + 0x96, 0x73, 0x07, 0xee, 0x23, 0xbe, 0xbe, 0x6e, 0x97, 0x98, 0x04, 0xdc, 0x55, 0xcb, 0x1f, 0xda, + 0x49, 0xb6, 0x10, 0xf0, 0x93, 0x40, 0x52, 0xc4, 0xef, 0xf0, 0xa2, 0x73, 0x85, 0x16, 0x76, 0xa3, + 0x86, 0x87, 0x4d, 0x70, 0x17, 0xea, 0x8f, 0xb5, 0xc1, 0x59, 0xb5, 0x9b, 0x8b, 0x2d, 0xfa, 0x50, + 0x0e, 0x51, 0x93, 0x32, 0x6e, 0x9b, 0xea, 0x8a, 0x8e, 0x39, 0x37, 0x3c, 0x28, 0xb5, 0xae, 0x61, + 0xcb, 0x8b, 0x43, 0xc4, 0x35, 0x93, 0x6d, 0x20, 0x99, 0xf8, 0x6a, 0x2f, 0x4c, 0xdf, 0x16, 0x78, + 0xb1, 0x42, 0x20, 0xe5, 0x9f, 0x3d, 0x34, 0xb7, 0xa3, 0xc4, 0x99, 0xf3, 0x98, 0x65, 0xc0, 0xb1, + 0xdb, 0x1c, 0xc0, 0xd1, 0x39, 0xc9, 0xa9, 0x58, 0x58, 0x11, 0x9f, 0x9c, 0x63, 0x4a, 0xc2, 0x8a, + 0x72, 0xd2, 0xd8, 0x6c, 0x2d, 0x25, 0x39, 0xa2, 0x94, 0xb1, 0xf0, 0xd5, 0x6a, 0xd7, 0xd6, 0xae, + 0x1a, 0xf7, 0x67, 0x85, 0x17, 0xe0, 0x71, 0xbb, 0x91, 0xc9, 0x65, 0x78, 0x32, 0x70, 0xc6, 0xb0, + 0x45, 0xb6, 0xb0, 0xa4, 0xbc, 0xd6, 0xdf, 0xcb, 0xe3, 0x97, 0xed, 0xab, 0xd2, 0xcb, 0xb3, 0x24, + 0x93, 0x3e, 0x1f, 0xdf, 0xcf, 0x0a, 0x68, 0xda, 0x59, 0x1d, 0x62, 0x94, 0x01, 0xcd, 0x00, 0x89, + 0x00, 0xd0, 0x54, 0xc9, 0x56, 0xf1, 0x62, 0x54, 0xa1, 0x65, 0xbc, 0xd6, 0xb2, 0xff, 0x91, 0x11, + 0x31, 0xf0, 0xbb, 0x0f, 0x1b, 0x16, 0xeb, 0x60, 0x53, 0xf9, 0x59, 0x6f, 0xe0, 0xf2, 0xe7, 0x34, + 0x69, 0x7b, 0xed, 0x3c, 0x36, 0x8e, 0x0b, 0xc0, 0xdb, 0x49, 0x82, 0xe2, 0x87, 0x78, 0xd7, 0xa0, + 0x8a, 0x32, 0xf6, 0xf5, 0xa7, 0x57, 0xf5, 0x99, 0xc6, 0x74, 0x8c, 0x39, 0xe1, 0x5c, 0xbd, 0xb1, + 0x50, 0x86, 0x8b, 0x52, 0x64, 0xf3, 0x6a, 0xe5, 0xb9, 0xd7, 0xca, 0xca, 0xef, 0xd1, 0x54, 0x8a, + 0x31, 0x82, 0x01, 0x1d, 0x30, 0x0d, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x11, + 0x02, 0x31, 0x00, 0x30, 0x13, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, + 0x31, 0x06, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00, 0x30, 0x69, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x11, 0x01, 0x31, 0x5c, 0x1e, 0x5a, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x41, 0x00, 0x20, 0x00, 0x53, 0x00, 0x43, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6e, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x43, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, + 0x74, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x72, 0x00, 0x61, 0x00, 0x70, 0x00, 0x68, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00, + 0x65, 0x00, 0x72, 0x30, 0x81, 0x8b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x14, 0x31, 0x7e, 0x1e, 0x7c, 0x00, 0x74, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x4b, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x41, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x63, 0x00, 0x33, 0x00, + 0x37, 0x00, 0x32, 0x00, 0x37, 0x00, 0x64, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x32, 0x00, + 0x33, 0x00, 0x62, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x37, 0x00, 0x32, 0x00, 0x63, 0x00, 0x2d, 0x00, + 0x61, 0x00, 0x34, 0x00, 0x66, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x61, 0x00, 0x36, 0x00, 0x31, 0x00, 0x66, 0x00, 0x62, 0x00, 0x65, 0x00, 0x33, 0x00, 0x31, 0x00, + 0x63, 0x30, 0x82, 0x0a, 0x7f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06, + 0xa0, 0x82, 0x0a, 0x70, 0x30, 0x82, 0x0a, 0x6c, 0x02, 0x01, 0x00, 0x30, 0x82, 0x0a, 0x65, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04, 0x08, 0xb6, 0x9d, 0x7d, 0x51, + 0xeb, 0xbb, 0x15, 0xc2, 0x02, 0x02, 0x07, 0xd0, 0x80, 0x82, 0x0a, 0x38, 0x46, 0x27, 0x8e, 0x12, + 0x17, 0xe1, 0x02, 0x90, 0xf5, 0x60, 0xb9, 0xb4, 0x9e, 0xd1, 0x60, 0xf9, 0xf2, 0x28, 0xbb, 0x4b, + 0xbb, 0xf5, 0x9f, 0xdf, 0x46, 0xb1, 0xca, 0x04, 0xa5, 0x08, 0xf2, 0xcd, 0xcc, 0x5c, 0xc6, 0x94, + 0x05, 0xf9, 0x93, 0x6d, 0x71, 0xb1, 0x5f, 0xc9, 0xc9, 0xd4, 0x64, 0xe4, 0xa7, 0x5c, 0x50, 0x5f, + 0x43, 0x14, 0x93, 0xfd, 0x4b, 0x8b, 0x62, 0x1b, 0xc1, 0xc6, 0x94, 0x19, 0xc1, 0x7e, 0xc6, 0x51, + 0x6b, 0x13, 0xf8, 0x9b, 0x49, 0xa4, 0x3f, 0xf3, 0x1e, 0xd0, 0x54, 0xc1, 0x6a, 0x35, 0x58, 0x6a, + 0x83, 0x38, 0x25, 0x88, 0x4c, 0xd9, 0xfc, 0x8a, 0xb6, 0x1b, 0xef, 0x36, 0x8b, 0x28, 0x21, 0x49, + 0xc5, 0x36, 0x9c, 0xc9, 0x74, 0x2e, 0xc6, 0x49, 0x9a, 0x0d, 0xa9, 0xa2, 0xf6, 0xc8, 0x06, 0x99, + 0xba, 0x54, 0x6f, 0x7e, 0xb3, 0xc8, 0x92, 0xaa, 0xa8, 0xe0, 0xf8, 0x5e, 0xac, 0x32, 0xbf, 0x19, + 0x9e, 0xf2, 0x9d, 0xa7, 0xb5, 0x7b, 0x32, 0x0a, 0x98, 0x25, 0xac, 0x58, 0x34, 0x7c, 0x2c, 0x91, + 0xaf, 0xfa, 0xdd, 0x1e, 0x91, 0x85, 0xe2, 0xe9, 0x8d, 0x8a, 0xb3, 0xbb, 0xff, 0x30, 0x37, 0xdb, + 0x4a, 0x35, 0x6e, 0x97, 0x61, 0x35, 0xc2, 0xa4, 0xba, 0x8d, 0xd1, 0xb1, 0xa3, 0x96, 0x03, 0xea, + 0x5d, 0x26, 0x6f, 0x50, 0x74, 0x4c, 0x64, 0x42, 0xf9, 0x09, 0x4f, 0xd4, 0x4e, 0x32, 0x12, 0xb1, + 0x0f, 0x3f, 0x5f, 0x1f, 0x6d, 0x60, 0x93, 0x79, 0x2d, 0x22, 0x28, 0x10, 0x40, 0x60, 0x1f, 0x89, + 0xbb, 0x4e, 0x4f, 0x0c, 0x77, 0xb2, 0x78, 0x27, 0x33, 0x4a, 0xf6, 0x4e, 0x83, 0x60, 0x7e, 0xdd, + 0x84, 0x36, 0x15, 0x06, 0xc8, 0x91, 0xd9, 0x05, 0xc9, 0x30, 0x04, 0xb7, 0xf2, 0x0d, 0x23, 0x18, + 0x00, 0x81, 0x1e, 0x00, 0xd7, 0xb4, 0xcc, 0x03, 0xba, 0x65, 0x12, 0x57, 0xad, 0x61, 0x41, 0x70, + 0x0b, 0x63, 0xad, 0x83, 0x78, 0xa0, 0xeb, 0x2b, 0xce, 0x97, 0x32, 0x79, 0x4d, 0x11, 0x94, 0x73, + 0x88, 0xb2, 0x0e, 0x67, 0x55, 0x79, 0x17, 0xa7, 0x22, 0x8c, 0x25, 0x2f, 0xbf, 0x07, 0x59, 0x3c, + 0x1d, 0xa8, 0xb3, 0xc6, 0xe8, 0x7b, 0x36, 0xbe, 0x99, 0xaa, 0x95, 0xb2, 0x2c, 0xe2, 0x7a, 0xae, + 0xb8, 0x19, 0xa2, 0xcb, 0xf7, 0xed, 0xe3, 0x90, 0x8d, 0xe1, 0x6f, 0xc3, 0x94, 0x05, 0xd2, 0xd0, + 0x7e, 0x1f, 0xd1, 0x6e, 0xe0, 0xe7, 0xca, 0x3b, 0xf5, 0xab, 0x77, 0xbb, 0xbf, 0x35, 0x1a, 0x59, + 0xbe, 0xf2, 0x31, 0x83, 0x61, 0xb5, 0xd5, 0x61, 0x35, 0x85, 0xfe, 0x50, 0xe9, 0x77, 0x98, 0x30, + 0x93, 0x50, 0x1e, 0x7f, 0xb2, 0x23, 0xab, 0xf7, 0xa5, 0xe7, 0x2c, 0x21, 0x6b, 0x64, 0x11, 0xf9, + 0x2e, 0x31, 0xc2, 0x07, 0x48, 0x75, 0xee, 0x3b, 0x49, 0x94, 0xa8, 0x23, 0x62, 0xaa, 0x56, 0x03, + 0xbf, 0xed, 0x77, 0x4f, 0x2b, 0xfe, 0x3d, 0xbf, 0x3f, 0xf3, 0x62, 0x3a, 0xad, 0xc1, 0x62, 0x2a, + 0x48, 0x33, 0x32, 0xd3, 0x3e, 0x3b, 0xe9, 0x5f, 0x03, 0x6f, 0xef, 0x18, 0x50, 0xa4, 0xe1, 0xfb, + 0x76, 0xd3, 0x2d, 0x94, 0x1b, 0x83, 0xde, 0xad, 0x6f, 0x83, 0x77, 0xa8, 0xe9, 0x2c, 0x43, 0x11, + 0x62, 0x69, 0xd4, 0x9f, 0xa6, 0xf8, 0x5d, 0x44, 0x5d, 0x59, 0x8d, 0xdb, 0x67, 0x5c, 0x4f, 0xdc, + 0xde, 0x08, 0x41, 0xb1, 0xe0, 0xb5, 0x06, 0xab, 0x12, 0x6a, 0xac, 0xe1, 0x1f, 0xd9, 0x7b, 0x05, + 0x20, 0x70, 0xd8, 0x1a, 0x37, 0x99, 0xd0, 0xb6, 0xc4, 0x5f, 0x8f, 0x58, 0x47, 0x16, 0xac, 0x87, + 0x92, 0xf4, 0x3f, 0x6e, 0x3c, 0x69, 0xb9, 0xad, 0x8c, 0x80, 0xde, 0x81, 0x16, 0xe7, 0x55, 0x13, + 0x3e, 0x34, 0xd7, 0x26, 0x5a, 0x27, 0x09, 0x1d, 0xd3, 0xa6, 0x3a, 0xaa, 0xb0, 0x59, 0x19, 0x19, + 0xff, 0xa0, 0x83, 0x89, 0xc8, 0x40, 0x71, 0x53, 0x88, 0xc1, 0x24, 0xa9, 0x20, 0xd4, 0x83, 0x79, + 0xf8, 0x05, 0xba, 0x24, 0xdd, 0x3c, 0x05, 0x7b, 0xf4, 0x47, 0xc0, 0x66, 0xb4, 0x89, 0xfa, 0xff, + 0x34, 0xa6, 0x7a, 0xbf, 0xa4, 0x20, 0xf2, 0x47, 0xbf, 0xf1, 0x2c, 0x8f, 0xda, 0x35, 0x4e, 0x75, + 0xa1, 0x37, 0x46, 0xf5, 0x1a, 0xfa, 0xe1, 0x99, 0x76, 0xfe, 0x24, 0x2c, 0x55, 0x24, 0xef, 0x95, + 0x30, 0x33, 0x34, 0x88, 0xb4, 0x29, 0xbd, 0xae, 0xfa, 0x44, 0xff, 0x8b, 0x74, 0xfe, 0x68, 0x95, + 0x57, 0x57, 0x3b, 0xd6, 0x85, 0xc8, 0x84, 0x5f, 0xad, 0x8a, 0x0f, 0x7e, 0xef, 0xa5, 0x12, 0xae, + 0x0e, 0x20, 0x8e, 0x10, 0x5b, 0xc3, 0x11, 0x89, 0xa7, 0xdb, 0x4d, 0x5e, 0xea, 0xf2, 0xc2, 0x02, + 0xea, 0xfe, 0x70, 0x61, 0xa9, 0xc9, 0x0c, 0x89, 0x51, 0x6e, 0x30, 0x9a, 0x4e, 0xe7, 0xe7, 0xa0, + 0xdf, 0x65, 0x72, 0x8e, 0x91, 0x79, 0x6d, 0xd1, 0x25, 0x95, 0xcf, 0xe2, 0x4e, 0x55, 0x23, 0xcc, + 0xcb, 0xc0, 0xf8, 0xc4, 0x1a, 0x62, 0xce, 0x8f, 0x23, 0x08, 0xd6, 0xfb, 0xd1, 0x91, 0x0d, 0xb6, + 0x07, 0x56, 0xab, 0x40, 0xe3, 0x5a, 0x26, 0x9d, 0x13, 0x57, 0x8a, 0x4d, 0x05, 0x1e, 0xa1, 0xa7, + 0xff, 0x20, 0xc8, 0x12, 0xcb, 0x70, 0xff, 0x0f, 0x28, 0xcd, 0xfe, 0x82, 0x56, 0x3d, 0x73, 0x6c, + 0xd1, 0x47, 0x24, 0xeb, 0xa2, 0x5b, 0xeb, 0xfe, 0xb9, 0x1b, 0x9a, 0x62, 0x0c, 0xa8, 0x97, 0xf5, + 0x34, 0x60, 0x32, 0x36, 0xbf, 0xa1, 0x96, 0x62, 0xdc, 0x7f, 0x77, 0x0c, 0x20, 0x6c, 0xaa, 0x52, + 0x90, 0x12, 0x40, 0x4b, 0x07, 0xc4, 0x7f, 0xc1, 0xf0, 0x4e, 0x1b, 0xeb, 0x69, 0xde, 0xd7, 0x54, + 0x2b, 0xc1, 0xeb, 0xdd, 0xd4, 0x98, 0x8a, 0xb0, 0xd8, 0x5e, 0xef, 0x2d, 0x0a, 0x89, 0xc8, 0xde, + 0x4d, 0x14, 0x2e, 0x9f, 0x26, 0x21, 0x72, 0xc6, 0x79, 0xff, 0x24, 0x06, 0xca, 0xbe, 0xb8, 0x10, + 0x7b, 0x0f, 0x01, 0xad, 0x0e, 0xb0, 0x7e, 0xb3, 0xab, 0x46, 0x38, 0xab, 0x27, 0x23, 0x8a, 0xa8, + 0x18, 0x4e, 0x2c, 0x9b, 0xb6, 0xcc, 0xb7, 0x2b, 0x11, 0x97, 0xcb, 0xb6, 0xb4, 0xa2, 0xe8, 0xf8, + 0xab, 0x54, 0x59, 0x8b, 0x26, 0x2e, 0x43, 0x72, 0x9e, 0x79, 0x5f, 0x71, 0x86, 0x24, 0x28, 0xb6, + 0xa2, 0x88, 0x88, 0x54, 0xd6, 0x62, 0x53, 0x28, 0x0c, 0x1b, 0xb2, 0x6a, 0x60, 0x05, 0x9a, 0xe1, + 0x07, 0x3d, 0xf3, 0xf1, 0xb1, 0x77, 0x2f, 0xf8, 0x5d, 0x3c, 0x26, 0x7b, 0x78, 0xa2, 0xde, 0x4b, + 0xcf, 0x60, 0xd9, 0x1b, 0xfa, 0x5b, 0xd1, 0xd0, 0x6d, 0x79, 0xf7, 0xbe, 0x44, 0x69, 0x90, 0x51, + 0x0e, 0xe2, 0x6e, 0xfd, 0x88, 0x8a, 0xf3, 0x79, 0x55, 0xae, 0x88, 0x09, 0x22, 0xf5, 0xdf, 0x61, + 0xa5, 0x3e, 0x0d, 0x4f, 0x2f, 0x6d, 0x1f, 0xd4, 0xe0, 0x69, 0x4f, 0x4e, 0x18, 0xb3, 0x42, 0x21, + 0x5f, 0x31, 0x38, 0x6d, 0x34, 0x5c, 0xa8, 0x31, 0x79, 0xdf, 0xb5, 0x59, 0x3a, 0xc6, 0x98, 0x50, + 0x1a, 0x36, 0xd9, 0xc3, 0x73, 0x2f, 0xd5, 0x7b, 0x37, 0x3b, 0xc0, 0x66, 0xf9, 0xb4, 0xec, 0x7c, + 0x26, 0x48, 0x0b, 0xee, 0x58, 0x9b, 0xdf, 0xd0, 0x87, 0x5e, 0x7f, 0x74, 0x60, 0xfb, 0xa6, 0xe3, + 0x1d, 0x38, 0x55, 0x84, 0x6c, 0x54, 0xb3, 0x73, 0x2d, 0xc6, 0x4b, 0x24, 0xb6, 0x05, 0x34, 0x65, + 0xeb, 0x50, 0xd3, 0x9a, 0x20, 0x98, 0xa2, 0xcd, 0xe7, 0x01, 0x24, 0xcd, 0x94, 0xf9, 0xb7, 0xf9, + 0x11, 0xef, 0xf1, 0x00, 0x53, 0xfd, 0x9d, 0xaf, 0x15, 0x49, 0xc6, 0xa7, 0xf4, 0x16, 0xa7, 0xfd, + 0x00, 0x36, 0x8d, 0x68, 0x8f, 0x5c, 0x69, 0xdb, 0x5c, 0x03, 0x4e, 0x14, 0xcc, 0x58, 0xea, 0xb1, + 0x6f, 0x21, 0x32, 0x39, 0xf8, 0xbd, 0x96, 0x6b, 0x99, 0xf2, 0x49, 0x4c, 0x89, 0xab, 0xc1, 0xe4, + 0x90, 0x37, 0xae, 0x07, 0x6f, 0xe2, 0x25, 0x94, 0xc8, 0x51, 0x25, 0xf1, 0x19, 0x00, 0x18, 0x30, + 0x58, 0x7b, 0x67, 0xc6, 0x13, 0xbf, 0x0f, 0x20, 0xd9, 0x50, 0x4c, 0x38, 0x18, 0x6f, 0x2a, 0x63, + 0x78, 0xc2, 0x45, 0xe7, 0xf2, 0x5d, 0x5a, 0xe6, 0xc2, 0x41, 0x66, 0x13, 0x25, 0xb0, 0x04, 0x17, + 0x76, 0xfc, 0x56, 0xfe, 0x88, 0x8a, 0x01, 0xee, 0x96, 0x39, 0x45, 0x17, 0x72, 0x90, 0x27, 0x14, + 0x59, 0x9a, 0x0c, 0xe1, 0xe5, 0xde, 0xac, 0x6b, 0xbd, 0x92, 0x7f, 0x4a, 0x01, 0x25, 0x17, 0x59, + 0xd8, 0xb3, 0x8b, 0x6f, 0x0f, 0x9a, 0x89, 0xf8, 0x37, 0xc2, 0xd7, 0x96, 0x02, 0x91, 0x8e, 0x6b, + 0xf3, 0xad, 0xd6, 0x6a, 0x73, 0xc3, 0x8d, 0xe5, 0xfa, 0xea, 0xa7, 0x1e, 0x95, 0xd2, 0x9d, 0xec, + 0xe1, 0x73, 0xe7, 0x43, 0xe7, 0x2d, 0x1f, 0xe3, 0x4e, 0x0f, 0xb0, 0x8e, 0x71, 0xec, 0xd0, 0x01, + 0x74, 0xb8, 0x2f, 0x0e, 0x96, 0xca, 0xf0, 0x59, 0x1f, 0xd8, 0x85, 0xa2, 0xa2, 0x6e, 0x42, 0x4a, + 0xb0, 0x17, 0xfd, 0xa2, 0x75, 0x60, 0x5f, 0xb2, 0xdd, 0x2d, 0xa7, 0xa5, 0xcb, 0x20, 0x99, 0xbf, + 0xfb, 0xab, 0xf7, 0x6a, 0x4d, 0x3d, 0xa4, 0x08, 0x12, 0x6f, 0x88, 0x92, 0xf7, 0x54, 0xa7, 0xc5, + 0xea, 0xbd, 0xb5, 0x38, 0x38, 0xf9, 0x2d, 0xbb, 0xb8, 0x3d, 0x4b, 0x78, 0x7c, 0xeb, 0x87, 0x3f, + 0x33, 0x32, 0x4b, 0x7a, 0xfc, 0x9a, 0xcf, 0xfe, 0xef, 0x01, 0x92, 0x38, 0xde, 0xa1, 0xf3, 0xb8, + 0x6e, 0x91, 0xcc, 0x5e, 0x4d, 0x13, 0x34, 0x2a, 0xc2, 0xc9, 0xdb, 0xc7, 0x74, 0x9f, 0x67, 0xe9, + 0xb9, 0x6d, 0x0b, 0x9d, 0x81, 0xa8, 0x58, 0x08, 0xde, 0x94, 0x1a, 0x04, 0xb7, 0xed, 0xb8, 0xf6, + 0x28, 0x8e, 0xb7, 0x06, 0xcf, 0x70, 0x6f, 0xfd, 0xc7, 0xf1, 0xee, 0x9f, 0x6f, 0xb1, 0x04, 0x71, + 0xc5, 0x52, 0x50, 0xbd, 0x47, 0xee, 0xbc, 0xe9, 0x8d, 0xaa, 0xe9, 0xf5, 0x26, 0xda, 0x99, 0x56, + 0x44, 0x3b, 0xd8, 0x3c, 0x04, 0x76, 0x89, 0x52, 0xdf, 0xa1, 0x26, 0x1f, 0xd4, 0xc4, 0x2b, 0xe9, + 0x0d, 0x0c, 0x16, 0x41, 0x66, 0xc1, 0x56, 0xc0, 0x39, 0xa0, 0x18, 0x78, 0xda, 0x1c, 0x4a, 0x8e, + 0xdf, 0x17, 0xe9, 0xf1, 0x8a, 0x72, 0x36, 0x66, 0xef, 0x60, 0x84, 0xa3, 0x34, 0xbc, 0x53, 0x21, + 0xe1, 0x87, 0x0b, 0x2d, 0x26, 0x5f, 0xeb, 0xc0, 0x2e, 0x14, 0xb8, 0x90, 0xbb, 0x2b, 0x22, 0xb6, + 0x8b, 0xde, 0xee, 0x10, 0xe3, 0x4b, 0x7f, 0xcd, 0x20, 0xa1, 0xaf, 0xa5, 0x8f, 0xb7, 0xa9, 0x62, + 0x27, 0xbe, 0x5f, 0x95, 0xed, 0x10, 0xb8, 0x76, 0x89, 0xbd, 0xb2, 0x1f, 0xde, 0x34, 0x94, 0x21, + 0x6b, 0x84, 0x2c, 0x18, 0x8d, 0x4f, 0xf4, 0x4c, 0x0a, 0xef, 0xad, 0xd6, 0x28, 0x32, 0x30, 0x57, + 0xa9, 0xc3, 0xb3, 0xf6, 0xbe, 0x0a, 0x8b, 0x3e, 0x9f, 0x72, 0x49, 0x22, 0x7b, 0x4a, 0xc5, 0xa7, + 0xf2, 0x89, 0x33, 0x83, 0xfa, 0x49, 0x98, 0x27, 0x7f, 0xa2, 0x4f, 0xce, 0xd1, 0xb3, 0x10, 0xfd, + 0xb6, 0x3c, 0x16, 0x43, 0x83, 0x0e, 0x9e, 0xde, 0xd2, 0x0f, 0x35, 0xa5, 0xc5, 0x61, 0x80, 0x6e, + 0x3f, 0xc4, 0xec, 0x35, 0x4a, 0x23, 0xe1, 0x4d, 0xed, 0x6b, 0x4b, 0x8a, 0xf6, 0xad, 0x2f, 0x4b, + 0xfe, 0x21, 0x55, 0xb9, 0xf7, 0xa5, 0x07, 0x1b, 0xa3, 0x34, 0xd2, 0x7c, 0x42, 0xe8, 0x78, 0x20, + 0xab, 0xe1, 0xa2, 0x34, 0x76, 0xfc, 0x4e, 0x80, 0x00, 0x52, 0xea, 0x09, 0xf0, 0xc3, 0x4e, 0xc1, + 0x16, 0x35, 0x38, 0x11, 0x1a, 0x1f, 0xaf, 0xfd, 0x50, 0x13, 0x4c, 0x10, 0xa9, 0x3a, 0x47, 0x00, + 0x96, 0x68, 0x2d, 0x68, 0xae, 0xb7, 0xe5, 0x0b, 0xba, 0x92, 0x37, 0x40, 0x8c, 0xf0, 0xf3, 0xc9, + 0xa6, 0x1c, 0xf3, 0x18, 0x96, 0x7d, 0xf0, 0xe6, 0xde, 0xcd, 0xb0, 0xef, 0x3e, 0x03, 0xc4, 0xf0, + 0x38, 0xf3, 0xfc, 0x4f, 0xd2, 0xa9, 0x33, 0xae, 0x6c, 0x48, 0x40, 0x37, 0x87, 0xa2, 0x9f, 0x03, + 0x93, 0xed, 0x7f, 0x96, 0x11, 0x67, 0x11, 0x7d, 0xb5, 0xb6, 0x84, 0x20, 0xb0, 0x04, 0x30, 0xfb, + 0xb9, 0x88, 0x3c, 0x17, 0x45, 0xf1, 0xac, 0x27, 0xad, 0x52, 0xe0, 0x0a, 0x97, 0xdc, 0x33, 0xd5, + 0x3f, 0x7a, 0x10, 0x29, 0xe5, 0x40, 0x1c, 0xf8, 0xde, 0x66, 0x6c, 0x04, 0x6b, 0x97, 0x3e, 0x20, + 0xbf, 0x59, 0x04, 0xd7, 0x12, 0xa2, 0xcc, 0x99, 0x3a, 0xf1, 0x5c, 0xc2, 0x1c, 0x9a, 0x0b, 0x03, + 0x0b, 0x9a, 0x61, 0x9d, 0xf2, 0xac, 0x85, 0x68, 0xd4, 0xd0, 0x79, 0x95, 0xa3, 0x82, 0x5e, 0x95, + 0x8c, 0xb4, 0x09, 0x45, 0xe4, 0x12, 0x17, 0x54, 0xb9, 0xf2, 0xaa, 0x82, 0xf7, 0xf7, 0x49, 0x95, + 0x28, 0xa8, 0xa8, 0xf9, 0x98, 0x07, 0x4f, 0x32, 0xeb, 0x96, 0xcb, 0x60, 0x9d, 0x82, 0x7c, 0x78, + 0x23, 0xa5, 0xc1, 0x94, 0x6b, 0x84, 0x64, 0x81, 0x89, 0xdd, 0xef, 0x66, 0xfc, 0x8f, 0x62, 0x16, + 0xc8, 0xe0, 0x17, 0xe3, 0x00, 0x85, 0x46, 0xb9, 0xad, 0xd9, 0x74, 0xce, 0x03, 0x10, 0xad, 0x89, + 0x85, 0xb0, 0x80, 0x86, 0xfe, 0x0e, 0x8d, 0x5b, 0x3e, 0x97, 0x7c, 0xec, 0x3b, 0xe7, 0xdb, 0x54, + 0xb3, 0xc5, 0xb7, 0xd2, 0x79, 0x87, 0x74, 0xa5, 0x59, 0x86, 0xc4, 0xb2, 0x1d, 0x9a, 0x91, 0x60, + 0x99, 0x5d, 0x01, 0xf1, 0x23, 0x5b, 0x72, 0xe0, 0xf8, 0x1e, 0x51, 0xf5, 0x2b, 0xb3, 0xe0, 0x07, + 0xee, 0x1d, 0x0a, 0xd2, 0x46, 0xe4, 0x3b, 0x29, 0x30, 0x9a, 0x26, 0xe9, 0x4d, 0x4c, 0x8f, 0x93, + 0x76, 0x16, 0xfe, 0xb7, 0x41, 0x5f, 0x10, 0xb0, 0xf3, 0x9e, 0x08, 0xc6, 0x35, 0xd1, 0xef, 0xaa, + 0x76, 0x2a, 0x30, 0xe5, 0x15, 0xa6, 0x06, 0x90, 0xd1, 0x88, 0x6b, 0x7a, 0xc6, 0xe4, 0xde, 0x44, + 0xeb, 0x41, 0x79, 0xe8, 0x74, 0x84, 0xbb, 0x7d, 0x01, 0x7b, 0x95, 0xfc, 0x05, 0x8a, 0x4a, 0xe1, + 0x75, 0x7f, 0x47, 0xed, 0x7d, 0xcf, 0x43, 0x35, 0x6b, 0x41, 0xb2, 0xbc, 0x21, 0x3a, 0xc2, 0x58, + 0x92, 0x1f, 0x85, 0x1d, 0xf4, 0x47, 0xb6, 0x72, 0x58, 0xc7, 0x28, 0xa6, 0xf1, 0x39, 0x65, 0x3e, + 0x5c, 0xea, 0x06, 0x8e, 0x64, 0x3b, 0xf9, 0x44, 0x02, 0xf5, 0xa3, 0xb0, 0xe8, 0x95, 0xf3, 0xbe, + 0x89, 0x29, 0x33, 0xba, 0x24, 0xa9, 0x08, 0x27, 0x34, 0x14, 0xfe, 0xd9, 0x36, 0x4c, 0x06, 0xce, + 0xef, 0xb0, 0x21, 0xfd, 0x57, 0x86, 0xaf, 0x25, 0x9e, 0xfc, 0xd5, 0xd6, 0x85, 0x3d, 0x4b, 0x80, + 0xc0, 0xbd, 0xfb, 0xc2, 0x03, 0xd8, 0xca, 0x70, 0xa4, 0xab, 0x52, 0x5c, 0xfb, 0x69, 0x72, 0x2b, + 0x0f, 0xf2, 0xc4, 0x6f, 0x19, 0x72, 0x4c, 0x95, 0x04, 0x94, 0x76, 0xaf, 0x1b, 0xe9, 0x10, 0x4b, + 0xe9, 0x49, 0xc9, 0x6b, 0x30, 0x83, 0x19, 0x31, 0xae, 0xe8, 0x52, 0xa5, 0x03, 0x2d, 0x9d, 0x6b, + 0x7e, 0x89, 0x5b, 0x62, 0x14, 0x32, 0xd4, 0xdb, 0x00, 0x73, 0x22, 0x69, 0x47, 0xa2, 0xcd, 0x17, + 0x7e, 0x48, 0x8e, 0x47, 0xc6, 0x74, 0x48, 0xf4, 0xb9, 0x9a, 0x1b, 0x67, 0x6a, 0xb0, 0xb2, 0xda, + 0x05, 0x74, 0x73, 0x7a, 0x92, 0x6f, 0xaf, 0xe9, 0xc3, 0x41, 0x41, 0xea, 0x20, 0xef, 0x1e, 0xbf, + 0x84, 0x9e, 0x84, 0xb4, 0x69, 0x69, 0x00, 0x76, 0xb5, 0xf7, 0xa9, 0x99, 0x69, 0x06, 0x7e, 0x89, + 0xa8, 0x70, 0x15, 0x3e, 0xf2, 0x78, 0xab, 0xd3, 0x57, 0x29, 0x49, 0x85, 0x10, 0xc7, 0x5b, 0x11, + 0x67, 0x90, 0x14, 0x57, 0x0b, 0xa6, 0xe5, 0xd2, 0xb5, 0x9e, 0x13, 0xda, 0x33, 0x4f, 0x4c, 0x42, + 0x28, 0x95, 0xc2, 0xd7, 0x20, 0xb4, 0xff, 0x68, 0xe2, 0x4a, 0xfe, 0xe8, 0x10, 0xa0, 0x21, 0x25, + 0x89, 0x4b, 0x75, 0xc5, 0xaa, 0xfc, 0x96, 0x16, 0xe4, 0xfb, 0xe3, 0x56, 0x14, 0xc0, 0x30, 0xab, + 0x18, 0x60, 0x72, 0x2d, 0x36, 0xaf, 0x18, 0x07, 0x76, 0x89, 0x7d, 0xe4, 0x07, 0xeb, 0x3d, 0x13, + 0xd1, 0xe8, 0x5a, 0x18, 0xd2, 0xa7, 0x42, 0x66, 0x91, 0x22, 0x58, 0x39, 0xe1, 0x67, 0x97, 0x95, + 0x81, 0x0a, 0x64, 0xdb, 0x6e, 0x92, 0x13, 0x2a, 0xe6, 0x35, 0x53, 0x18, 0x52, 0x4f, 0xc0, 0x38, + 0x43, 0x30, 0x04, 0x34, 0x03, 0x06, 0xa6, 0x90, 0xcf, 0xd4, 0x0f, 0xdb, 0xd9, 0x41, 0xeb, 0xd2, + 0x54, 0x21, 0x88, 0x3b, 0x08, 0xd9, 0xe1, 0xcc, 0x87, 0xa3, 0x6d, 0x31, 0x85, 0x05, 0xf4, 0x82, + 0x70, 0x49, 0x7f, 0x2b, 0xe3, 0xe8, 0x40, 0xf3, 0xa4, 0x48, 0x5e, 0x4a, 0x8d, 0x91, 0xa9, 0x5e, + 0x08, 0x6b, 0xd4, 0x63, 0x45, 0x8f, 0x78, 0xe0, 0x7f, 0xcb, 0x61, 0x81, 0x50, 0xc0, 0x83, 0xc3, + 0x7f, 0x72, 0x2f, 0x15, 0x5e, 0x0c, 0x29, 0x9f, 0x7f, 0xb8, 0xf5, 0x66, 0x08, 0xb7, 0xb4, 0xee, + 0x9c, 0xd6, 0xac, 0x83, 0x4f, 0x46, 0xf0, 0x0d, 0x3e, 0x4a, 0x2b, 0x4d, 0x55, 0xbb, 0xc7, 0x71, + 0xf3, 0x6a, 0x0f, 0x40, 0xb3, 0xf5, 0xd7, 0xfb, 0x26, 0x2a, 0x73, 0xe4, 0xac, 0x31, 0x63, 0xe4, + 0xd3, 0xef, 0xed, 0xed, 0x48, 0xc5, 0xe6, 0xdb, 0xf3, 0x5e, 0x7c, 0x00, 0x57, 0x17, 0xc7, 0xab, + 0x9f, 0xe2, 0x52, 0x4b, 0xfd, 0x1a, 0x37, 0x53, 0x27, 0xe8, 0x4b, 0x97, 0xbe, 0xb4, 0xc2, 0x79, + 0x2e, 0x6a, 0x10, 0x87, 0xbb, 0x3c, 0x2c, 0xd4, 0xbc, 0xda, 0x70, 0xe1, 0xf3, 0x51, 0x96, 0xfe, + 0xfd, 0xd3, 0x28, 0x20, 0xe3, 0x79, 0x7d, 0x92, 0x70, 0x7d, 0x10, 0x14, 0x57, 0x14, 0x43, 0x46, + 0x2b, 0x09, 0x15, 0x9b, 0x28, 0x2f, 0x67, 0xbf, 0xc7, 0xf4, 0xad, 0x77, 0xd0, 0xb7, 0xca, 0xc3, + 0x5c, 0x79, 0x84, 0xb5, 0x4f, 0x20, 0x6b, 0xdb, 0x3d, 0x73, 0x81, 0x0e, 0x64, 0xbd, 0x44, 0x86, + 0xf0, 0xc6, 0xce, 0xf5, 0x4b, 0xbc, 0x10, 0x7b, 0xee, 0x97, 0x48, 0xf2, 0x48, 0x95, 0xd8, 0x17, + 0xda, 0xd9, 0x78, 0x72, 0x39, 0x54, 0x8f, 0x72, 0x49, 0xca, 0x03, 0x33, 0x8d, 0xe0, 0xaa, 0x9c, + 0xca, 0x53, 0xff, 0x34, 0x78, 0xba, 0x10, 0x76, 0x32, 0x3b, 0xcd, 0xd1, 0x2b, 0x17, 0x26, 0x41, + 0x16, 0x1b, 0xba, 0xfa, 0x84, 0xd9, 0x2a, 0x81, 0xd0, 0x73, 0x7d, 0x02, 0x32, 0x24, 0xea, 0xd2, + 0x9a, 0xa6, 0xa5, 0x76, 0x48, 0x9a, 0xd8, 0xf7, 0x58, 0xa5, 0x3c, 0x95, 0xc8, 0x1f, 0x23, 0x3e, + 0xef, 0x0b, 0xff, 0x75, 0x28, 0xcf, 0x7f, 0xea, 0xc3, 0x37, 0x0a, 0xe8, 0xb2, 0x85, 0x26, 0x3a, + 0x8c, 0x9c, 0x40, 0x18, 0x55, 0xeb, 0x65, 0x06, 0x9e, 0x65, 0x32, 0x42, 0x5c, 0x03, 0x1a, 0x2d, + 0x0d, 0x18, 0xd9, 0x08, 0xcc, 0x6a, 0x96, 0xc3, 0x0f, 0x17, 0x0a, 0x00, 0x74, 0x00, 0x7c, 0xf7, + 0x6a, 0x48, 0x81, 0xd0, 0xcc, 0xb9, 0xf0, 0x5f, 0x70, 0xd5, 0xe6, 0x53, 0x67, 0x82, 0x75, 0x61, + 0x0d, 0x5e, 0xbc, 0x7d, 0xa1, 0xa0, 0xd0, 0x59, 0xc5, 0xb6, 0x97, 0x4c, 0xd8, 0xdf, 0x21, 0x07, + 0xcd, 0x9b, 0x8d, 0xb8, 0xdf, 0x40, 0x98, 0xe2, 0x32, 0xbf, 0x5e, 0x32, 0xb4, 0xbf, 0xd3, 0x5b, + 0x3b, 0x7d, 0x1e, 0x3e, 0x66, 0x89, 0x51, 0xc8, 0xc9, 0xee, 0xa1, 0xc7, 0x62, 0x68, 0x79, 0x65, + 0x24, 0x2d, 0xc1, 0x6b, 0xde, 0x2b, 0x01, 0xec, 0x26, 0xfa, 0x99, 0xb3, 0x6e, 0x6c, 0xda, 0x24, + 0xb3, 0x69, 0x80, 0x3f, 0xf2, 0xfe, 0xac, 0xad, 0x9e, 0x95, 0xf8, 0xe3, 0xa7, 0x34, 0x6f, 0xa4, + 0xff, 0x14, 0x53, 0xe0, 0x3a, 0xfd, 0x47, 0xa8, 0x2e, 0x79, 0x28, 0x0b, 0xb5, 0x10, 0xc7, 0xca, + 0x70, 0x6f, 0x28, 0x27, 0xed, 0x84, 0x70, 0x71, 0x80, 0x8b, 0x85, 0xe6, 0xdd, 0xf4, 0xc8, 0x41, + 0x18, 0xee, 0xff, 0xc3, 0x30, 0x3b, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, + 0x1a, 0x04, 0x14, 0x33, 0x17, 0x2b, 0x61, 0x77, 0xe7, 0xca, 0x9d, 0x35, 0xbb, 0xcc, 0x11, 0xce, + 0xb3, 0x3c, 0x8c, 0xcb, 0x38, 0xd6, 0xfc, 0x04, 0x14, 0x74, 0x81, 0x8c, 0x45, 0x9e, 0x8e, 0xd2, + 0x42, 0x5f, 0x6b, 0xae, 0x1a, 0x5d, 0x8f, 0x73, 0x1f, 0xc3, 0xbd, 0x8d, 0x8f, 0x02, 0x02, 0x07, + 0xd0 }; + + public static X509Certificate2 Certificate { get; } = new X509Certificate2(kdcPfxData); +} \ No newline at end of file diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs new file mode 100644 index 00000000000000..27796a22b34be6 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Buffers.Binary; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakeKdcServer +{ + private readonly KdcServer kdcServer; + private readonly TcpListener tcpListener; + private CancellationTokenSource? cancellationTokenSource; + private bool running; + private readonly object runningLock; + + public FakeKdcServer(KdcServerOptions serverOptions) + { + kdcServer = new KdcServer(serverOptions); + tcpListener = new TcpListener(System.Net.IPAddress.Loopback, 0); + runningLock = new object(); + } + + public Task Start() + { + cancellationTokenSource = new CancellationTokenSource(); + running = true; + tcpListener.Start(); + + var cancellationToken = cancellationTokenSource.Token; + Task.Run(async () => { + try + { + byte[] sizeBuffer = new byte[4]; + do + { + using var socket = await tcpListener.AcceptSocketAsync(cancellationToken); + using var socketStream = new NetworkStream(socket); + + await socketStream.ReadExactlyAsync(sizeBuffer, cancellationToken); + var messageSize = BinaryPrimitives.ReadInt32BigEndian(sizeBuffer); + var requestRented = ArrayPool.Shared.Rent(messageSize); + var request = requestRented.AsMemory(0, messageSize); + await socketStream.ReadExactlyAsync(request); + var response = await kdcServer.ProcessMessage(request); + ArrayPool.Shared.Return(requestRented); + var responseLength = response.Length + 4; + var responseRented = ArrayPool.Shared.Rent(responseLength); + BinaryPrimitives.WriteInt32BigEndian(responseRented.AsSpan(0, 4), responseLength); + response.CopyTo(responseRented.AsMemory(4, responseLength)); + await socketStream.WriteAsync(responseRented.AsMemory(0, responseLength + 4), cancellationToken); + ArrayPool.Shared.Return(responseRented); + } + while (!cancellationToken.IsCancellationRequested); + } + finally + { + lock (runningLock) + { + running = false; + Monitor.Pulse(runningLock); + } + } + }); + return Task.FromResult((IPEndPoint)tcpListener.LocalEndpoint); + } + + public void Stop() + { + if (running) + { + cancellationTokenSource?.Cancel(); + lock (runningLock) + { + while (running) + { + Monitor.Wait(runningLock); + } + } + tcpListener.Stop(); + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs new file mode 100644 index 00000000000000..01e0b72eb22d06 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Entities.Pac; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakeKerberosPrincipal : IKerberosPrincipal +{ + private static readonly byte[] KrbTgtKey = new byte[] + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + private static readonly SecurityIdentifier DomainSid = new( + IdentifierAuthority.NTAuthority, + new uint[] { 123, 456, 789, 012, 321 }, + 0 + ); + + private readonly SecurityIdentifier userSid = new(DomainSid, 888); + + private readonly SecurityIdentifier groupSid = new(DomainSid, 513); + + internal static readonly byte[] FakePassword = Encoding.Unicode.GetBytes("P@ssw0rd!"); + + public FakeKerberosPrincipal(string principalName, string realm) + { + this.PrincipalName = principalName; + this.Realm = realm; + this.Expires = DateTimeOffset.UtcNow.AddMonths(9999); + } + + public SupportedEncryptionTypes SupportedEncryptionTypes { get; set; } + = SupportedEncryptionTypes.Aes128CtsHmacSha196 | + SupportedEncryptionTypes.Aes256CtsHmacSha196 | + SupportedEncryptionTypes.Aes128CtsHmacSha256 | + SupportedEncryptionTypes.Aes256CtsHmacSha384 | + SupportedEncryptionTypes.Rc4Hmac | + SupportedEncryptionTypes.DesCbcCrc | + SupportedEncryptionTypes.DesCbcMd5; + + public IEnumerable SupportedPreAuthenticationTypes { get; set; } = new[] + { + PaDataType.PA_ENC_TIMESTAMP, + PaDataType.PA_PK_AS_REQ + }; + + public PrincipalType Type + { + get + { + if (this.PrincipalName == "krbtgt" || this.PrincipalName.Equals($"krbtgt/{Realm}", StringComparison.CurrentCultureIgnoreCase)) + { + return PrincipalType.Service; + } + + if (this.PrincipalName.StartsWith("krbtgt/", StringComparison.InvariantCultureIgnoreCase)) + { + return PrincipalType.TrustedDomain; + } + + if (this.PrincipalName.Contains('/')) + { + return PrincipalType.Service; + } + + return PrincipalType.User; + } + } + + public string PrincipalName { get; private set; } + + public string Realm { get; private set; } + + public DateTimeOffset? Expires { get; set; } + + public PrivilegedAttributeCertificate GeneratePac() + { + var pac = new PrivilegedAttributeCertificate() + { + LogonInfo = new PacLogonInfo + { + DomainName = Realm, + UserName = PrincipalName, + UserDisplayName = PrincipalName, + BadPasswordCount = 12, + SubAuthStatus = 0, + DomainSid = DomainSid, + UserSid = userSid, + GroupSid = groupSid, + LogonTime = DateTimeOffset.UtcNow, + ServerName = "server", + UserAccountControl = UserAccountControlFlags.ADS_UF_NORMAL_ACCOUNT, + UserFlags = UserFlags.LOGON_WINLOGON, + + } + }; + + return pac; + } + + private KerberosKey? TgtKey { get; set; } + + private static readonly ConcurrentDictionary KeyCache = new(); + + public KerberosKey RetrieveLongTermCredential() + { + EncryptionType etype = ExtractEType(this.PrincipalName); + + return this.RetrieveLongTermCredential(etype); + } + + public KerberosKey RetrieveLongTermCredential(EncryptionType etype) + { + KerberosKey key; + + if (this.PrincipalName.StartsWith("krbtgt", StringComparison.InvariantCultureIgnoreCase)) + { + key = this.TgtKey ?? (this.TgtKey = new( + password: KrbTgtKey, + principal: new PrincipalName(PrincipalNameType.NT_PRINCIPAL, Realm, new[] { "krbtgt" }), + etype: EncryptionType.AES256_CTS_HMAC_SHA1_96, + saltType: SaltType.ActiveDirectoryUser + )); + } + else + { + key = KeyCache.GetOrAdd(etype + this.PrincipalName, pn => + { + return new KerberosKey( + password: FakePassword, + principal: new PrincipalName(PrincipalNameType.NT_PRINCIPAL, Realm, new[] { this.PrincipalName }), + etype: etype, + saltType: SaltType.ActiveDirectoryUser + ); + }); + } + + return key; + } + + private static EncryptionType ExtractEType(string principalName) + { + if (principalName.StartsWith("RC4", StringComparison.InvariantCultureIgnoreCase)) + { + return EncryptionType.RC4_HMAC_NT; + } + else if (principalName.StartsWith("AES128SHA256", StringComparison.InvariantCultureIgnoreCase)) + { + return EncryptionType.AES128_CTS_HMAC_SHA256_128; + } + else if (principalName.StartsWith("AES128", StringComparison.InvariantCultureIgnoreCase)) + { + return EncryptionType.AES128_CTS_HMAC_SHA1_96; + } + else if (principalName.StartsWith("AES256SHA384", StringComparison.InvariantCultureIgnoreCase)) + { + return EncryptionType.AES256_CTS_HMAC_SHA384_192; + } + else if (principalName.StartsWith("AES256", StringComparison.InvariantCultureIgnoreCase)) + { + return EncryptionType.AES256_CTS_HMAC_SHA1_96; + } + + return EncryptionType.AES256_CTS_HMAC_SHA1_96; + } + + public void Validate(X509Certificate2Collection certificates) + { + } +} diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs new file mode 100644 index 00000000000000..4dfeee5a512acd --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakePrincipalService : IPrincipalService +{ + private readonly string realm; + + public FakePrincipalService(string realm) + { + this.realm = realm; + } + + public Task FindAsync(KrbPrincipalName principalName, string? realm = null) + { + return Task.FromResult(Find(principalName, realm)); + } + + public IKerberosPrincipal? Find(KrbPrincipalName principalName, string? realm = null) + { + IKerberosPrincipal? principal = null; + + bool fallback = false; + + if (principalName.FullyQualifiedName.Contains("-fallback", StringComparison.OrdinalIgnoreCase) && + principalName.Type == PrincipalNameType.NT_ENTERPRISE) + { + principal = null; + fallback = true; + } + + if ((principalName.FullyQualifiedName.EndsWith(this.realm, StringComparison.InvariantCultureIgnoreCase) || + principalName.FullyQualifiedName.StartsWith("krbtgt", StringComparison.InvariantCultureIgnoreCase) || + principalName.Type == PrincipalNameType.NT_PRINCIPAL) + && !fallback) + { + principal = new FakeKerberosPrincipal(principalName.FullyQualifiedName, this.realm); + } + + return principal; + } + + public X509Certificate2 RetrieveKdcCertificate() + { + return FakeKdcCertificate.Certificate; + } + + private static readonly Dictionary KeyCache = new(); + + public IExchangeKey? RetrieveKeyCache(KeyAgreementAlgorithm algorithm) + { + if (KeyCache.TryGetValue(algorithm, out IExchangeKey? key)) + { + if (key.CacheExpiry < DateTimeOffset.UtcNow) + { + KeyCache.Remove(algorithm); + } + else + { + return key; + } + } + + return null; + } + + public IExchangeKey CacheKey(IExchangeKey key) + { + key.CacheExpiry = DateTimeOffset.UtcNow.AddMinutes(60); + + KeyCache[key.Algorithm] = key; + + return key; + } +} diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs new file mode 100644 index 00000000000000..61803aad5ae81d --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Kerberos.NET.Entities; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakeRealmReferral : IRealmReferral +{ + private readonly KrbKdcReqBody body; + + public FakeRealmReferral(KrbKdcReqBody body) + { + this.body = body; + } + + public IKerberosPrincipal Refer() + { + var fqn = this.body.SName.FullyQualifiedName; + var predictedRealm = fqn[(fqn.IndexOf('.') + 1)..]; + + var krbName = KrbPrincipalName.FromString($"krbtgt/{predictedRealm}"); + + return new FakeKerberosPrincipal(krbName.FullyQualifiedName, predictedRealm); + } +} diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs new file mode 100644 index 00000000000000..e6ee0c34d10e3f --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Kerberos.NET.Configuration; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakeRealmService : IRealmService +{ + private readonly KerberosCompatibilityFlags compatibilityFlags; + + public FakeRealmService(string realm, Krb5Config? config = null, KerberosCompatibilityFlags compatibilityFlags = KerberosCompatibilityFlags.None) + { + this.Name = realm; + this.Configuration = config ?? Krb5Config.Kdc(); + this.compatibilityFlags = compatibilityFlags; + } + + public IRealmSettings Settings => new FakeRealmSettings(this.compatibilityFlags); + + public IPrincipalService Principals => new FakePrincipalService(this.Name); + + public string Name { get; } + + public DateTimeOffset Now() => DateTimeOffset.UtcNow; + + public ITrustedRealmService TrustedRealms => new FakeTrustedRealms(this.Name); + + public Krb5Config Configuration { get; } +} \ No newline at end of file diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs new file mode 100644 index 00000000000000..60ab60127d96b4 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakeRealmSettings : IRealmSettings +{ + private readonly KerberosCompatibilityFlags compatibilityFlags; + + public FakeRealmSettings(KerberosCompatibilityFlags compatibilityFlags) + { + this.compatibilityFlags = compatibilityFlags; + } + + public TimeSpan MaximumSkew => TimeSpan.FromMinutes(5); + + public TimeSpan SessionLifetime => TimeSpan.FromHours(10); + + public TimeSpan MaximumRenewalWindow => TimeSpan.FromDays(7); + + public KerberosCompatibilityFlags Compatibility => this.compatibilityFlags; +} diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs new file mode 100644 index 00000000000000..de54ebf8f6451b --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Kerberos.NET.Entities; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +class FakeTrustedRealms : ITrustedRealmService +{ + private readonly string currentRealm; + + public FakeTrustedRealms(string name) + { + this.currentRealm = name; + } + + public IRealmReferral? ProposeTransit(KrbTgsReq tgsReq, PreAuthenticationContext context) + { + if (!tgsReq.Body.SName.FullyQualifiedName.EndsWith(this.currentRealm, StringComparison.InvariantCultureIgnoreCase) && + !tgsReq.Body.SName.FullyQualifiedName.Contains("not.found")) + { + return new FakeRealmReferral(tgsReq.Body); + } + + return null; + } +} diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs new file mode 100644 index 00000000000000..e624f50bb684fb --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.RemoteExecutor; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Server; + +namespace System.Net.Security.Kerberos; + +public class KerberosExecutor : IDisposable +{ + private ListenerOptions _options; + private string _realm; + private FakeKdcServer _kdcListener; + private RemoteInvokeHandle? _invokeHandle; + private string? _krb5Path; + private string? _keytabPath; + private List _services; + + public static bool IsSupported { get; } = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); + public KerberosExecutor(string realm = "linux.contoso.com") + { + _options = new ListenerOptions + { + DefaultRealm = realm.ToUpper(), + RealmLocator = realm => new FakeRealmService(realm) + }; + + _kdcListener = new FakeKdcServer(_options); + _services = new List(); + _realm = realm; + } + + public void Dispose() + { + _invokeHandle?.Dispose(); + _kdcListener.Stop(); + File.Delete(_krb5Path); + File.Delete(_keytabPath); + } + + public void AddService(string name) + { + _services.Add(name); + } + + public async Task Invoke(Action method) + { + await PrepareInvoke(); + _invokeHandle = RemoteExecutor.Invoke(method, "user", "P@ssw0rd!", _options.DefaultRealm); + } + + public async Task Invoke(Func method) + { + await PrepareInvoke(); + _invokeHandle = RemoteExecutor.Invoke(method, "user", "P@ssw0rd!", _options.DefaultRealm); + } + + private async Task PrepareInvoke() + { + // Start the KDC server + var endpoint = await _kdcListener.Start(); + + // Generate krb5.conf + _krb5Path = Path.GetTempFileName(); + File.WriteAllText(_krb5Path, + OperatingSystem.IsLinux() ? + $"[realms]\n{_options.DefaultRealm} = {{\n master_kdc = {endpoint}\n kdc = {endpoint}\n}}\n" : + $"[realms]\n{_options.DefaultRealm} = {{\n kdc = tcp/{endpoint}\n}}\n"); + + // Generate keytab file + _keytabPath = Path.GetTempFileName(); + var keyTable = new KeyTable(); + + var etypes = _options.Configuration.Defaults.DefaultTgsEncTypes; + byte[] passwordBytes = FakeKerberosPrincipal.FakePassword; + + foreach (string service in _services) + { + foreach (var etype in etypes.Where(CryptoService.SupportsEType)) + { + var kerbKey = new KerberosKey( + password: passwordBytes, + etype: etype, + principal: new PrincipalName( + PrincipalNameType.NT_PRINCIPAL, + _options.DefaultRealm, + new [] { $"{service}/{_realm}" }), + saltType: SaltType.ActiveDirectoryUser + ); + + keyTable.Entries.Add(new KeyEntry(kerbKey)); + } + } + + using (var fs = new FileStream(_keytabPath, FileMode.Create)) + using (var writer = new BinaryWriter(fs)) + { + keyTable.Write(writer); + writer.Flush(); + } + + // Set environment variables for GSSAPI + Environment.SetEnvironmentVariable("KRB5_CONFIG", _krb5Path); + Environment.SetEnvironmentVariable("KRB5_KTNAME", _keytabPath); + } +} \ No newline at end of file diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems b/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems new file mode 100644 index 00000000000000..c5b4a144189e57 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems @@ -0,0 +1,44 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 4aaf81e6-6cdc-44fa-adfd-d7b47b9c998f + + + + true + + + + + CommonTest\System\Net\Security\Kerberos\FakeKdcCertificate.cs + + + CommonTest\System\Net\Security\Kerberos\FakeKdcServer.cs + + + CommonTest\System\Net\Security\Kerberos\FakeKerberosPrincipal.cs + + + CommonTest\System\Net\Security\Kerberos\FakePrincipalService.cs + + + CommonTest\System\Net\Security\Kerberos\FakeRealmReferral.cs + + + CommonTest\System\Net\Security\Kerberos\FakeRealmService.cs + + + CommonTest\System\Net\Security\Kerberos\FakeRealmSettings.cs + + + CommonTest\System\Net\Security\Kerberos\FakeTrustedRealms.cs + + + CommonTest\System\Net\Security\Kerberos\KerberosExecutor.cs + + + + + + diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs new file mode 100644 index 00000000000000..51b36a0596c9ff --- /dev/null +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.IO; +using System.Net.Security; +using System.Text; +using System.Threading.Tasks; +using System.Net.Test.Common; +using System.Net.Security.Kerberos; +using Xunit; + +namespace System.Net.Security.Tests +{ + [ConditionalClass(typeof(KerberosExecutor), nameof(KerberosExecutor.IsSupported))] + public class NegotiateAuthenticationKerberosTest + { + [Fact] + public async Task Loopback_Kerberos_Authentication() + { + using var kerberosExecutor = new KerberosExecutor(); + + kerberosExecutor.AddService("HTTP"); + + await kerberosExecutor.Invoke((user, password, realm) => + { + // Do a loopback authentication + NegotiateAuthenticationClientOptions clientOptions = new() + { + Credential = new NetworkCredential(user, password, realm), + TargetName = $"HTTP/{realm}" + }; + NegotiateAuthenticationServerOptions serverOptions = new() { }; + NegotiateAuthentication clientNegotiateAuthentication = new(clientOptions); + NegotiateAuthentication serverNegotiateAuthentication = new(serverOptions); + + byte[]? serverBlob = null; + byte[]? clientBlob = null; + bool shouldContinue = true; + do + { + clientBlob = clientNegotiateAuthentication.GetOutgoingBlob(serverBlob, out NegotiateAuthenticationStatusCode statusCode); + shouldContinue = statusCode == NegotiateAuthenticationStatusCode.ContinueNeeded; + Assert.True(statusCode <= NegotiateAuthenticationStatusCode.ContinueNeeded, $"Client authentication failed with {statusCode}"); + if (clientBlob != null) + { + serverBlob = serverNegotiateAuthentication.GetOutgoingBlob(clientBlob, out statusCode); + Assert.True(statusCode <= NegotiateAuthenticationStatusCode.ContinueNeeded, $"Server authentication failed with {statusCode}"); + } + } + while (serverBlob != null && shouldContinue); + + Assert.Equal("Kerberos", clientNegotiateAuthentication.Package); + Assert.Equal("Kerberos", serverNegotiateAuthentication.Package); + Assert.True(clientNegotiateAuthentication.IsAuthenticated); + Assert.True(serverNegotiateAuthentication.IsAuthenticated); + }); + } + } +} diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 742c49799eaf9b..2c5f9d30fdcfb4 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -9,6 +9,7 @@ + @@ -28,6 +29,8 @@ + + From 12b65a1b014359a877b5232157ef1417cffcfa5b Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 11 Jul 2022 16:36:52 +0000 Subject: [PATCH 02/11] Revert NuGet.config changes --- NuGet.config | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/NuGet.config b/NuGet.config index d46359cd51223b..a6d878497eacd0 100644 --- a/NuGet.config +++ b/NuGet.config @@ -21,17 +21,7 @@ - - - - - - - - - - From f95e6e25bc76aef405dcf88071a696e23569e8a4 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 8 Jul 2022 14:26:56 +0200 Subject: [PATCH 03/11] Be explicit about casing and realm name --- .../Net/Security/Kerberos/KerberosExecutor.cs | 17 ++++++++++------- .../NegotiateAuthenticationKerberosTest.cs | 10 +++++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs index e624f50bb684fb..29ea22c79400fd 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs @@ -26,11 +26,14 @@ public class KerberosExecutor : IDisposable private List _services; public static bool IsSupported { get; } = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); - public KerberosExecutor(string realm = "linux.contoso.com") + + public static string FakePassword { get; } = "P@ssw0rd!"; + + public KerberosExecutor(string realm) { _options = new ListenerOptions { - DefaultRealm = realm.ToUpper(), + DefaultRealm = realm, RealmLocator = realm => new FakeRealmService(realm) }; @@ -52,16 +55,16 @@ public void AddService(string name) _services.Add(name); } - public async Task Invoke(Action method) + public async Task Invoke(Action method) { await PrepareInvoke(); - _invokeHandle = RemoteExecutor.Invoke(method, "user", "P@ssw0rd!", _options.DefaultRealm); + _invokeHandle = RemoteExecutor.Invoke(method); } - public async Task Invoke(Func method) + public async Task Invoke(Func method) { await PrepareInvoke(); - _invokeHandle = RemoteExecutor.Invoke(method, "user", "P@ssw0rd!", _options.DefaultRealm); + _invokeHandle = RemoteExecutor.Invoke(method); } private async Task PrepareInvoke() @@ -93,7 +96,7 @@ private async Task PrepareInvoke() principal: new PrincipalName( PrincipalNameType.NT_PRINCIPAL, _options.DefaultRealm, - new [] { $"{service}/{_realm}" }), + new [] { service }), saltType: SaltType.ActiveDirectoryUser ); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs index 51b36a0596c9ff..e6c4e5d394d52d 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs @@ -20,17 +20,17 @@ public class NegotiateAuthenticationKerberosTest [Fact] public async Task Loopback_Kerberos_Authentication() { - using var kerberosExecutor = new KerberosExecutor(); + using var kerberosExecutor = new KerberosExecutor("LINUX.CONTOSO.COM"); - kerberosExecutor.AddService("HTTP"); + kerberosExecutor.AddService("HTTP/linux.contoso.com"); - await kerberosExecutor.Invoke((user, password, realm) => + await kerberosExecutor.Invoke(() => { // Do a loopback authentication NegotiateAuthenticationClientOptions clientOptions = new() { - Credential = new NetworkCredential(user, password, realm), - TargetName = $"HTTP/{realm}" + Credential = new NetworkCredential("user", KerberosExecutor.FakePassword, "LINUX.CONTOSO.COM"), + TargetName = $"HTTP/linux.contoso.com" }; NegotiateAuthenticationServerOptions serverOptions = new() { }; NegotiateAuthentication clientNegotiateAuthentication = new(clientOptions); From ff0c20ce5f2929b9378463e845c8c7598d2f39d4 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 11 Jul 2022 17:20:43 +0000 Subject: [PATCH 04/11] Remove PKINIT code from fakes --- .../Security/Kerberos/FakeKdcCertificate.cs | 289 ------------------ .../Security/Kerberos/FakePrincipalService.cs | 2 +- .../Net/Security/Kerberos/FakeRealmService.cs | 4 +- .../Net/Security/Kerberos/KerberosExecutor.cs | 7 +- ...tem.Net.Security.Kerberos.Shared.projitems | 3 - 5 files changed, 9 insertions(+), 296 deletions(-) delete mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs deleted file mode 100644 index 9bb69ffac24b5b..00000000000000 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcCertificate.cs +++ /dev/null @@ -1,289 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Security.Cryptography.X509Certificates; - -namespace System.Net.Security.Kerberos; - -class FakeKdcCertificate -{ - static ReadOnlySpan kdcPfxData => new byte[] { - 0x30, 0x82, 0x11, 0x2d, 0x02, 0x01, 0x03, 0x30, 0x82, 0x10, 0xe9, 0x06, 0x09, 0x2a, 0x86, 0x48, - 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, 0x10, 0xda, 0x04, 0x82, 0x10, 0xd6, 0x30, 0x82, - 0x10, 0xd2, 0x30, 0x82, 0x06, 0x4b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, - 0x01, 0xa0, 0x82, 0x06, 0x3c, 0x04, 0x82, 0x06, 0x38, 0x30, 0x82, 0x06, 0x34, 0x30, 0x82, 0x06, - 0x30, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, - 0x04, 0xfe, 0x30, 0x82, 0x04, 0xfa, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04, 0x08, 0x3a, 0x2a, 0x8d, 0x7f, 0x36, 0x9e, 0xfd, 0xaa, - 0x02, 0x02, 0x07, 0xd0, 0x04, 0x82, 0x04, 0xd8, 0xb8, 0x64, 0x7a, 0x53, 0xcf, 0x06, 0x10, 0xc8, - 0xdd, 0xab, 0x43, 0xdd, 0x82, 0x8a, 0x56, 0xe7, 0xee, 0x54, 0x3a, 0x43, 0x4e, 0xe7, 0x63, 0xd5, - 0xc5, 0x49, 0xd4, 0x59, 0xfa, 0x54, 0x27, 0x38, 0xf2, 0x8c, 0xc5, 0xe9, 0xf1, 0x20, 0xe2, 0x32, - 0xcb, 0x64, 0xe4, 0xd8, 0xb2, 0xe1, 0x72, 0x79, 0xc9, 0xb5, 0x2f, 0x5a, 0x9b, 0xb4, 0x09, 0x20, - 0x4f, 0x9d, 0x5c, 0x26, 0x48, 0xc6, 0x89, 0xb9, 0x87, 0xc0, 0xda, 0xb8, 0x5b, 0x18, 0x77, 0x2b, - 0x35, 0x95, 0x89, 0x36, 0x19, 0x36, 0xe6, 0xb5, 0x00, 0x52, 0x3d, 0x2b, 0x40, 0x59, 0x1d, 0xb1, - 0xa1, 0x59, 0x8e, 0x13, 0xe1, 0x39, 0x6e, 0xd5, 0xa9, 0xc8, 0xfd, 0x8b, 0x41, 0xeb, 0x44, 0xca, - 0x08, 0xa1, 0x1a, 0xf5, 0xce, 0x66, 0xf6, 0x7f, 0xba, 0xf3, 0x73, 0x58, 0xf6, 0x58, 0x10, 0x45, - 0x5e, 0xaa, 0x86, 0x7f, 0x2c, 0x88, 0x39, 0xab, 0x87, 0x95, 0xd1, 0x0e, 0x7b, 0x60, 0xa8, 0x90, - 0x2b, 0xa6, 0xb9, 0xeb, 0x1c, 0x15, 0x13, 0xf7, 0xef, 0xac, 0x88, 0x0f, 0x7b, 0x8f, 0xbb, 0xcd, - 0x4b, 0x22, 0x87, 0xea, 0xe7, 0x81, 0xa8, 0x06, 0x74, 0x0a, 0xd5, 0x7f, 0xe1, 0x5b, 0x77, 0x81, - 0x9d, 0x71, 0xeb, 0x21, 0x5d, 0x25, 0x1d, 0x05, 0x19, 0x4b, 0x93, 0x79, 0x94, 0x24, 0x5e, 0xa7, - 0xe2, 0x85, 0x3d, 0xf7, 0xdc, 0xf4, 0xec, 0x0e, 0x89, 0x7c, 0xb9, 0xbc, 0xfc, 0xc2, 0xdf, 0xac, - 0x8d, 0x86, 0xe8, 0xe2, 0x0a, 0xc8, 0x3f, 0xa8, 0x45, 0xa1, 0x87, 0x15, 0xe4, 0x20, 0x5e, 0xff, - 0xf1, 0x11, 0x51, 0x23, 0xf1, 0x08, 0x2b, 0x4f, 0x80, 0x98, 0xeb, 0xc7, 0xe5, 0x92, 0xeb, 0xa3, - 0xd2, 0xeb, 0x1f, 0x18, 0x23, 0x7a, 0x5c, 0x0e, 0x26, 0xc1, 0x90, 0xcc, 0x95, 0x1b, 0xea, 0x69, - 0x9e, 0x15, 0x6c, 0xef, 0xc8, 0x21, 0xb7, 0x04, 0x89, 0x49, 0x2b, 0x6e, 0xa6, 0xf6, 0x67, 0x7c, - 0x75, 0xb2, 0x98, 0x77, 0xdb, 0x0c, 0x28, 0xaa, 0x5d, 0x46, 0x4c, 0x3c, 0x18, 0x6a, 0x87, 0x9d, - 0xf8, 0xee, 0x24, 0x52, 0xe2, 0xd2, 0xc9, 0x4e, 0x98, 0x39, 0x4c, 0xe6, 0x95, 0x12, 0x4c, 0x9b, - 0x96, 0x0f, 0xb5, 0xb7, 0xb0, 0x28, 0x13, 0x3c, 0xf7, 0xb9, 0xa2, 0x8a, 0xc8, 0xa9, 0x8d, 0xe8, - 0x4f, 0x96, 0x80, 0xf5, 0x2e, 0x78, 0xe9, 0x62, 0xf7, 0x9b, 0xbb, 0xb0, 0x4c, 0x5c, 0xd0, 0xcf, - 0x51, 0xcb, 0xac, 0x1a, 0xe6, 0x4f, 0x33, 0x54, 0x00, 0xb7, 0xd2, 0x05, 0xc2, 0x1d, 0xe9, 0xf0, - 0x3e, 0x85, 0x7c, 0x28, 0x97, 0xdb, 0xe8, 0xaa, 0x6d, 0xc2, 0xb5, 0x6e, 0xf1, 0x54, 0x30, 0xc0, - 0x53, 0x56, 0x9b, 0x1a, 0xb2, 0x6e, 0xb9, 0x9a, 0xc7, 0xed, 0xbc, 0x57, 0x07, 0x27, 0x2a, 0x44, - 0x18, 0x6d, 0xb8, 0x81, 0x80, 0xcf, 0x6e, 0xe3, 0x8f, 0x23, 0x4b, 0xcc, 0x7d, 0xdd, 0x22, 0x5a, - 0x90, 0x77, 0xc0, 0xc9, 0xd4, 0xad, 0x94, 0xf1, 0xa4, 0x41, 0x5f, 0x50, 0xf1, 0x0c, 0x8d, 0xa8, - 0xe3, 0xc5, 0x0a, 0x3f, 0x9b, 0xf2, 0x06, 0xa0, 0xdb, 0xef, 0x19, 0x72, 0x3f, 0x8d, 0x6e, 0xf8, - 0x90, 0xd8, 0x6f, 0x24, 0x42, 0xc3, 0xf4, 0x34, 0xe5, 0x4b, 0xe1, 0xa9, 0x5d, 0xe3, 0xbc, 0x57, - 0x13, 0x14, 0x2e, 0x72, 0xb5, 0x7c, 0xf2, 0x34, 0x79, 0x2c, 0x9b, 0x95, 0x0b, 0xd6, 0x33, 0x1a, - 0xbe, 0x7d, 0xf9, 0x0f, 0x6e, 0xe8, 0x9a, 0x37, 0x0f, 0x8f, 0xde, 0x0f, 0xb0, 0x30, 0xbe, 0xe8, - 0x20, 0x1c, 0xb9, 0xcd, 0xd6, 0x57, 0xbc, 0x30, 0xb2, 0x8e, 0x25, 0xec, 0x5f, 0xb0, 0x64, 0xc7, - 0x7d, 0x0e, 0xe2, 0xb8, 0x52, 0xb5, 0xa8, 0x37, 0x94, 0xb3, 0x8c, 0x9c, 0x18, 0x3d, 0xb7, 0xca, - 0x37, 0x16, 0xac, 0xca, 0x7b, 0xee, 0xe7, 0x14, 0xb1, 0x8e, 0x28, 0x57, 0x05, 0xd3, 0x30, 0xcd, - 0x98, 0xe8, 0xdd, 0xc5, 0x64, 0xa5, 0x36, 0x86, 0x8d, 0x04, 0xc2, 0x79, 0x4a, 0x8b, 0x4a, 0x36, - 0x67, 0x75, 0x48, 0x6a, 0x41, 0x7b, 0x5e, 0x36, 0xeb, 0x4f, 0xd2, 0xe7, 0xb5, 0x41, 0x7b, 0xbd, - 0x5a, 0xfb, 0xe8, 0x64, 0xfb, 0x0a, 0x6a, 0x20, 0x9e, 0x5f, 0x3d, 0x9e, 0x98, 0x57, 0xd0, 0xea, - 0x7a, 0xee, 0xa0, 0x2b, 0x89, 0x2e, 0xee, 0x5b, 0xf6, 0xd3, 0x31, 0xa3, 0x59, 0xda, 0x25, 0xbf, - 0x9f, 0xcf, 0xd5, 0x69, 0xba, 0xc8, 0xf6, 0x7f, 0x41, 0x3a, 0x2d, 0xc1, 0x34, 0xdf, 0x7e, 0x0c, - 0x24, 0xec, 0x76, 0x8d, 0x2d, 0xc7, 0x40, 0xb3, 0xa4, 0xe3, 0x00, 0x92, 0xd8, 0x8d, 0xf0, 0x27, - 0xa6, 0xee, 0x17, 0x3e, 0x30, 0xb9, 0x05, 0x4f, 0x0e, 0x9e, 0xb4, 0x47, 0xe9, 0xb5, 0xe5, 0xfe, - 0xad, 0xae, 0xf1, 0xc4, 0xaa, 0xdd, 0x46, 0x74, 0x41, 0xc6, 0x37, 0xf5, 0xe3, 0xd3, 0xd7, 0x2b, - 0x45, 0x9a, 0x24, 0x5e, 0xc2, 0xde, 0x77, 0xdf, 0x17, 0xc8, 0xd5, 0x62, 0xa0, 0xb7, 0x70, 0xbf, - 0x53, 0x55, 0xf0, 0xfa, 0xaa, 0x9b, 0x64, 0x8e, 0xcc, 0x9c, 0xf9, 0xa8, 0x05, 0x9e, 0xbd, 0x07, - 0xe6, 0x1d, 0xbe, 0x57, 0x4f, 0xeb, 0x38, 0xa2, 0x44, 0x87, 0x95, 0x0c, 0x26, 0xe5, 0x43, 0x2a, - 0x02, 0xc3, 0x32, 0xdb, 0x61, 0x36, 0xa9, 0x06, 0xd9, 0x75, 0x9b, 0x50, 0x7f, 0x31, 0xb8, 0xc4, - 0x6c, 0x1c, 0x76, 0x94, 0xae, 0xf8, 0xdc, 0x50, 0x62, 0x51, 0x07, 0xba, 0xe0, 0xac, 0x58, 0xc5, - 0x50, 0xd4, 0xd9, 0xbd, 0x4d, 0xcc, 0x2f, 0x2e, 0xdb, 0x7c, 0x8b, 0xa1, 0xb5, 0xc8, 0x76, 0x29, - 0xb9, 0x8b, 0xba, 0xb5, 0x37, 0xea, 0xb6, 0xa7, 0x4c, 0x5b, 0x27, 0x3d, 0x1e, 0x2f, 0xf8, 0x2e, - 0x3e, 0x49, 0xf0, 0x17, 0xc3, 0x08, 0xd3, 0xe7, 0x28, 0x84, 0x7c, 0xee, 0x84, 0xd9, 0x71, 0x4a, - 0x17, 0x3c, 0xfd, 0x0f, 0x80, 0xf5, 0xcd, 0xe4, 0x53, 0xcd, 0x38, 0xe7, 0x26, 0x3a, 0x39, 0xb4, - 0x14, 0x9b, 0x23, 0xfe, 0x12, 0x72, 0x99, 0xe2, 0xe8, 0xd9, 0x06, 0x26, 0x35, 0x2d, 0xc9, 0x0e, - 0x3c, 0x41, 0x0f, 0x24, 0x1c, 0xdb, 0x0d, 0x05, 0xdb, 0xe3, 0x05, 0x4a, 0x8c, 0x8d, 0xe1, 0x8d, - 0xbd, 0x71, 0x0a, 0xf9, 0xf1, 0x47, 0x8b, 0x62, 0x65, 0x50, 0x9d, 0xe2, 0x41, 0x57, 0x7f, 0x02, - 0x3a, 0x26, 0xc7, 0xd2, 0xd6, 0xdd, 0xdc, 0xa9, 0xc5, 0x8b, 0xb0, 0x0e, 0x56, 0x14, 0x44, 0x2a, - 0x19, 0x01, 0x99, 0xba, 0x0c, 0xdf, 0x4e, 0x06, 0xf2, 0x34, 0x7c, 0x33, 0x32, 0xed, 0xe0, 0x59, - 0xd8, 0x3e, 0x0a, 0x46, 0xac, 0x05, 0x75, 0xc7, 0x57, 0xf1, 0x25, 0xa9, 0x9b, 0xd8, 0xdb, 0x98, - 0xee, 0xe0, 0xd3, 0xcb, 0xda, 0xd2, 0x1a, 0x68, 0x4d, 0xa2, 0x20, 0x12, 0x86, 0x0c, 0x11, 0xdb, - 0x0c, 0x79, 0xe6, 0xdd, 0xd5, 0xb6, 0x60, 0xe7, 0xfa, 0xca, 0xd0, 0x2c, 0xb2, 0x7d, 0xf9, 0x9f, - 0xf4, 0x95, 0xfe, 0xcd, 0xb1, 0xa5, 0x89, 0xfe, 0xfc, 0x48, 0x15, 0xe0, 0x52, 0x3f, 0x01, 0x72, - 0xfb, 0x05, 0x65, 0x69, 0x8d, 0x00, 0x39, 0x5c, 0x25, 0xea, 0xb7, 0x63, 0xa9, 0xb9, 0x64, 0x13, - 0x4e, 0x75, 0x43, 0xaf, 0x60, 0x97, 0x2c, 0xf3, 0x0c, 0x5a, 0x5f, 0xc9, 0x05, 0x01, 0xfe, 0x51, - 0xfb, 0x10, 0xa6, 0x8a, 0x6d, 0xab, 0x57, 0x43, 0x53, 0x5e, 0xc2, 0x6d, 0xfb, 0x37, 0xce, 0xe8, - 0x96, 0x73, 0x07, 0xee, 0x23, 0xbe, 0xbe, 0x6e, 0x97, 0x98, 0x04, 0xdc, 0x55, 0xcb, 0x1f, 0xda, - 0x49, 0xb6, 0x10, 0xf0, 0x93, 0x40, 0x52, 0xc4, 0xef, 0xf0, 0xa2, 0x73, 0x85, 0x16, 0x76, 0xa3, - 0x86, 0x87, 0x4d, 0x70, 0x17, 0xea, 0x8f, 0xb5, 0xc1, 0x59, 0xb5, 0x9b, 0x8b, 0x2d, 0xfa, 0x50, - 0x0e, 0x51, 0x93, 0x32, 0x6e, 0x9b, 0xea, 0x8a, 0x8e, 0x39, 0x37, 0x3c, 0x28, 0xb5, 0xae, 0x61, - 0xcb, 0x8b, 0x43, 0xc4, 0x35, 0x93, 0x6d, 0x20, 0x99, 0xf8, 0x6a, 0x2f, 0x4c, 0xdf, 0x16, 0x78, - 0xb1, 0x42, 0x20, 0xe5, 0x9f, 0x3d, 0x34, 0xb7, 0xa3, 0xc4, 0x99, 0xf3, 0x98, 0x65, 0xc0, 0xb1, - 0xdb, 0x1c, 0xc0, 0xd1, 0x39, 0xc9, 0xa9, 0x58, 0x58, 0x11, 0x9f, 0x9c, 0x63, 0x4a, 0xc2, 0x8a, - 0x72, 0xd2, 0xd8, 0x6c, 0x2d, 0x25, 0x39, 0xa2, 0x94, 0xb1, 0xf0, 0xd5, 0x6a, 0xd7, 0xd6, 0xae, - 0x1a, 0xf7, 0x67, 0x85, 0x17, 0xe0, 0x71, 0xbb, 0x91, 0xc9, 0x65, 0x78, 0x32, 0x70, 0xc6, 0xb0, - 0x45, 0xb6, 0xb0, 0xa4, 0xbc, 0xd6, 0xdf, 0xcb, 0xe3, 0x97, 0xed, 0xab, 0xd2, 0xcb, 0xb3, 0x24, - 0x93, 0x3e, 0x1f, 0xdf, 0xcf, 0x0a, 0x68, 0xda, 0x59, 0x1d, 0x62, 0x94, 0x01, 0xcd, 0x00, 0x89, - 0x00, 0xd0, 0x54, 0xc9, 0x56, 0xf1, 0x62, 0x54, 0xa1, 0x65, 0xbc, 0xd6, 0xb2, 0xff, 0x91, 0x11, - 0x31, 0xf0, 0xbb, 0x0f, 0x1b, 0x16, 0xeb, 0x60, 0x53, 0xf9, 0x59, 0x6f, 0xe0, 0xf2, 0xe7, 0x34, - 0x69, 0x7b, 0xed, 0x3c, 0x36, 0x8e, 0x0b, 0xc0, 0xdb, 0x49, 0x82, 0xe2, 0x87, 0x78, 0xd7, 0xa0, - 0x8a, 0x32, 0xf6, 0xf5, 0xa7, 0x57, 0xf5, 0x99, 0xc6, 0x74, 0x8c, 0x39, 0xe1, 0x5c, 0xbd, 0xb1, - 0x50, 0x86, 0x8b, 0x52, 0x64, 0xf3, 0x6a, 0xe5, 0xb9, 0xd7, 0xca, 0xca, 0xef, 0xd1, 0x54, 0x8a, - 0x31, 0x82, 0x01, 0x1d, 0x30, 0x0d, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x11, - 0x02, 0x31, 0x00, 0x30, 0x13, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, - 0x31, 0x06, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00, 0x30, 0x69, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, - 0x01, 0x82, 0x37, 0x11, 0x01, 0x31, 0x5c, 0x1e, 0x5a, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x52, 0x00, - 0x53, 0x00, 0x41, 0x00, 0x20, 0x00, 0x53, 0x00, 0x43, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6e, 0x00, - 0x6e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x43, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, - 0x74, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x72, 0x00, 0x61, 0x00, 0x70, 0x00, 0x68, 0x00, 0x69, 0x00, - 0x63, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00, - 0x65, 0x00, 0x72, 0x30, 0x81, 0x8b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, - 0x14, 0x31, 0x7e, 0x1e, 0x7c, 0x00, 0x74, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x4b, 0x00, 0x65, 0x00, - 0x72, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x41, 0x00, 0x75, 0x00, - 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x63, 0x00, 0x33, 0x00, - 0x37, 0x00, 0x32, 0x00, 0x37, 0x00, 0x64, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x32, 0x00, - 0x33, 0x00, 0x62, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x37, 0x00, 0x32, 0x00, 0x63, 0x00, 0x2d, 0x00, - 0x61, 0x00, 0x34, 0x00, 0x66, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, - 0x61, 0x00, 0x36, 0x00, 0x31, 0x00, 0x66, 0x00, 0x62, 0x00, 0x65, 0x00, 0x33, 0x00, 0x31, 0x00, - 0x63, 0x30, 0x82, 0x0a, 0x7f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06, - 0xa0, 0x82, 0x0a, 0x70, 0x30, 0x82, 0x0a, 0x6c, 0x02, 0x01, 0x00, 0x30, 0x82, 0x0a, 0x65, 0x06, - 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, - 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04, 0x08, 0xb6, 0x9d, 0x7d, 0x51, - 0xeb, 0xbb, 0x15, 0xc2, 0x02, 0x02, 0x07, 0xd0, 0x80, 0x82, 0x0a, 0x38, 0x46, 0x27, 0x8e, 0x12, - 0x17, 0xe1, 0x02, 0x90, 0xf5, 0x60, 0xb9, 0xb4, 0x9e, 0xd1, 0x60, 0xf9, 0xf2, 0x28, 0xbb, 0x4b, - 0xbb, 0xf5, 0x9f, 0xdf, 0x46, 0xb1, 0xca, 0x04, 0xa5, 0x08, 0xf2, 0xcd, 0xcc, 0x5c, 0xc6, 0x94, - 0x05, 0xf9, 0x93, 0x6d, 0x71, 0xb1, 0x5f, 0xc9, 0xc9, 0xd4, 0x64, 0xe4, 0xa7, 0x5c, 0x50, 0x5f, - 0x43, 0x14, 0x93, 0xfd, 0x4b, 0x8b, 0x62, 0x1b, 0xc1, 0xc6, 0x94, 0x19, 0xc1, 0x7e, 0xc6, 0x51, - 0x6b, 0x13, 0xf8, 0x9b, 0x49, 0xa4, 0x3f, 0xf3, 0x1e, 0xd0, 0x54, 0xc1, 0x6a, 0x35, 0x58, 0x6a, - 0x83, 0x38, 0x25, 0x88, 0x4c, 0xd9, 0xfc, 0x8a, 0xb6, 0x1b, 0xef, 0x36, 0x8b, 0x28, 0x21, 0x49, - 0xc5, 0x36, 0x9c, 0xc9, 0x74, 0x2e, 0xc6, 0x49, 0x9a, 0x0d, 0xa9, 0xa2, 0xf6, 0xc8, 0x06, 0x99, - 0xba, 0x54, 0x6f, 0x7e, 0xb3, 0xc8, 0x92, 0xaa, 0xa8, 0xe0, 0xf8, 0x5e, 0xac, 0x32, 0xbf, 0x19, - 0x9e, 0xf2, 0x9d, 0xa7, 0xb5, 0x7b, 0x32, 0x0a, 0x98, 0x25, 0xac, 0x58, 0x34, 0x7c, 0x2c, 0x91, - 0xaf, 0xfa, 0xdd, 0x1e, 0x91, 0x85, 0xe2, 0xe9, 0x8d, 0x8a, 0xb3, 0xbb, 0xff, 0x30, 0x37, 0xdb, - 0x4a, 0x35, 0x6e, 0x97, 0x61, 0x35, 0xc2, 0xa4, 0xba, 0x8d, 0xd1, 0xb1, 0xa3, 0x96, 0x03, 0xea, - 0x5d, 0x26, 0x6f, 0x50, 0x74, 0x4c, 0x64, 0x42, 0xf9, 0x09, 0x4f, 0xd4, 0x4e, 0x32, 0x12, 0xb1, - 0x0f, 0x3f, 0x5f, 0x1f, 0x6d, 0x60, 0x93, 0x79, 0x2d, 0x22, 0x28, 0x10, 0x40, 0x60, 0x1f, 0x89, - 0xbb, 0x4e, 0x4f, 0x0c, 0x77, 0xb2, 0x78, 0x27, 0x33, 0x4a, 0xf6, 0x4e, 0x83, 0x60, 0x7e, 0xdd, - 0x84, 0x36, 0x15, 0x06, 0xc8, 0x91, 0xd9, 0x05, 0xc9, 0x30, 0x04, 0xb7, 0xf2, 0x0d, 0x23, 0x18, - 0x00, 0x81, 0x1e, 0x00, 0xd7, 0xb4, 0xcc, 0x03, 0xba, 0x65, 0x12, 0x57, 0xad, 0x61, 0x41, 0x70, - 0x0b, 0x63, 0xad, 0x83, 0x78, 0xa0, 0xeb, 0x2b, 0xce, 0x97, 0x32, 0x79, 0x4d, 0x11, 0x94, 0x73, - 0x88, 0xb2, 0x0e, 0x67, 0x55, 0x79, 0x17, 0xa7, 0x22, 0x8c, 0x25, 0x2f, 0xbf, 0x07, 0x59, 0x3c, - 0x1d, 0xa8, 0xb3, 0xc6, 0xe8, 0x7b, 0x36, 0xbe, 0x99, 0xaa, 0x95, 0xb2, 0x2c, 0xe2, 0x7a, 0xae, - 0xb8, 0x19, 0xa2, 0xcb, 0xf7, 0xed, 0xe3, 0x90, 0x8d, 0xe1, 0x6f, 0xc3, 0x94, 0x05, 0xd2, 0xd0, - 0x7e, 0x1f, 0xd1, 0x6e, 0xe0, 0xe7, 0xca, 0x3b, 0xf5, 0xab, 0x77, 0xbb, 0xbf, 0x35, 0x1a, 0x59, - 0xbe, 0xf2, 0x31, 0x83, 0x61, 0xb5, 0xd5, 0x61, 0x35, 0x85, 0xfe, 0x50, 0xe9, 0x77, 0x98, 0x30, - 0x93, 0x50, 0x1e, 0x7f, 0xb2, 0x23, 0xab, 0xf7, 0xa5, 0xe7, 0x2c, 0x21, 0x6b, 0x64, 0x11, 0xf9, - 0x2e, 0x31, 0xc2, 0x07, 0x48, 0x75, 0xee, 0x3b, 0x49, 0x94, 0xa8, 0x23, 0x62, 0xaa, 0x56, 0x03, - 0xbf, 0xed, 0x77, 0x4f, 0x2b, 0xfe, 0x3d, 0xbf, 0x3f, 0xf3, 0x62, 0x3a, 0xad, 0xc1, 0x62, 0x2a, - 0x48, 0x33, 0x32, 0xd3, 0x3e, 0x3b, 0xe9, 0x5f, 0x03, 0x6f, 0xef, 0x18, 0x50, 0xa4, 0xe1, 0xfb, - 0x76, 0xd3, 0x2d, 0x94, 0x1b, 0x83, 0xde, 0xad, 0x6f, 0x83, 0x77, 0xa8, 0xe9, 0x2c, 0x43, 0x11, - 0x62, 0x69, 0xd4, 0x9f, 0xa6, 0xf8, 0x5d, 0x44, 0x5d, 0x59, 0x8d, 0xdb, 0x67, 0x5c, 0x4f, 0xdc, - 0xde, 0x08, 0x41, 0xb1, 0xe0, 0xb5, 0x06, 0xab, 0x12, 0x6a, 0xac, 0xe1, 0x1f, 0xd9, 0x7b, 0x05, - 0x20, 0x70, 0xd8, 0x1a, 0x37, 0x99, 0xd0, 0xb6, 0xc4, 0x5f, 0x8f, 0x58, 0x47, 0x16, 0xac, 0x87, - 0x92, 0xf4, 0x3f, 0x6e, 0x3c, 0x69, 0xb9, 0xad, 0x8c, 0x80, 0xde, 0x81, 0x16, 0xe7, 0x55, 0x13, - 0x3e, 0x34, 0xd7, 0x26, 0x5a, 0x27, 0x09, 0x1d, 0xd3, 0xa6, 0x3a, 0xaa, 0xb0, 0x59, 0x19, 0x19, - 0xff, 0xa0, 0x83, 0x89, 0xc8, 0x40, 0x71, 0x53, 0x88, 0xc1, 0x24, 0xa9, 0x20, 0xd4, 0x83, 0x79, - 0xf8, 0x05, 0xba, 0x24, 0xdd, 0x3c, 0x05, 0x7b, 0xf4, 0x47, 0xc0, 0x66, 0xb4, 0x89, 0xfa, 0xff, - 0x34, 0xa6, 0x7a, 0xbf, 0xa4, 0x20, 0xf2, 0x47, 0xbf, 0xf1, 0x2c, 0x8f, 0xda, 0x35, 0x4e, 0x75, - 0xa1, 0x37, 0x46, 0xf5, 0x1a, 0xfa, 0xe1, 0x99, 0x76, 0xfe, 0x24, 0x2c, 0x55, 0x24, 0xef, 0x95, - 0x30, 0x33, 0x34, 0x88, 0xb4, 0x29, 0xbd, 0xae, 0xfa, 0x44, 0xff, 0x8b, 0x74, 0xfe, 0x68, 0x95, - 0x57, 0x57, 0x3b, 0xd6, 0x85, 0xc8, 0x84, 0x5f, 0xad, 0x8a, 0x0f, 0x7e, 0xef, 0xa5, 0x12, 0xae, - 0x0e, 0x20, 0x8e, 0x10, 0x5b, 0xc3, 0x11, 0x89, 0xa7, 0xdb, 0x4d, 0x5e, 0xea, 0xf2, 0xc2, 0x02, - 0xea, 0xfe, 0x70, 0x61, 0xa9, 0xc9, 0x0c, 0x89, 0x51, 0x6e, 0x30, 0x9a, 0x4e, 0xe7, 0xe7, 0xa0, - 0xdf, 0x65, 0x72, 0x8e, 0x91, 0x79, 0x6d, 0xd1, 0x25, 0x95, 0xcf, 0xe2, 0x4e, 0x55, 0x23, 0xcc, - 0xcb, 0xc0, 0xf8, 0xc4, 0x1a, 0x62, 0xce, 0x8f, 0x23, 0x08, 0xd6, 0xfb, 0xd1, 0x91, 0x0d, 0xb6, - 0x07, 0x56, 0xab, 0x40, 0xe3, 0x5a, 0x26, 0x9d, 0x13, 0x57, 0x8a, 0x4d, 0x05, 0x1e, 0xa1, 0xa7, - 0xff, 0x20, 0xc8, 0x12, 0xcb, 0x70, 0xff, 0x0f, 0x28, 0xcd, 0xfe, 0x82, 0x56, 0x3d, 0x73, 0x6c, - 0xd1, 0x47, 0x24, 0xeb, 0xa2, 0x5b, 0xeb, 0xfe, 0xb9, 0x1b, 0x9a, 0x62, 0x0c, 0xa8, 0x97, 0xf5, - 0x34, 0x60, 0x32, 0x36, 0xbf, 0xa1, 0x96, 0x62, 0xdc, 0x7f, 0x77, 0x0c, 0x20, 0x6c, 0xaa, 0x52, - 0x90, 0x12, 0x40, 0x4b, 0x07, 0xc4, 0x7f, 0xc1, 0xf0, 0x4e, 0x1b, 0xeb, 0x69, 0xde, 0xd7, 0x54, - 0x2b, 0xc1, 0xeb, 0xdd, 0xd4, 0x98, 0x8a, 0xb0, 0xd8, 0x5e, 0xef, 0x2d, 0x0a, 0x89, 0xc8, 0xde, - 0x4d, 0x14, 0x2e, 0x9f, 0x26, 0x21, 0x72, 0xc6, 0x79, 0xff, 0x24, 0x06, 0xca, 0xbe, 0xb8, 0x10, - 0x7b, 0x0f, 0x01, 0xad, 0x0e, 0xb0, 0x7e, 0xb3, 0xab, 0x46, 0x38, 0xab, 0x27, 0x23, 0x8a, 0xa8, - 0x18, 0x4e, 0x2c, 0x9b, 0xb6, 0xcc, 0xb7, 0x2b, 0x11, 0x97, 0xcb, 0xb6, 0xb4, 0xa2, 0xe8, 0xf8, - 0xab, 0x54, 0x59, 0x8b, 0x26, 0x2e, 0x43, 0x72, 0x9e, 0x79, 0x5f, 0x71, 0x86, 0x24, 0x28, 0xb6, - 0xa2, 0x88, 0x88, 0x54, 0xd6, 0x62, 0x53, 0x28, 0x0c, 0x1b, 0xb2, 0x6a, 0x60, 0x05, 0x9a, 0xe1, - 0x07, 0x3d, 0xf3, 0xf1, 0xb1, 0x77, 0x2f, 0xf8, 0x5d, 0x3c, 0x26, 0x7b, 0x78, 0xa2, 0xde, 0x4b, - 0xcf, 0x60, 0xd9, 0x1b, 0xfa, 0x5b, 0xd1, 0xd0, 0x6d, 0x79, 0xf7, 0xbe, 0x44, 0x69, 0x90, 0x51, - 0x0e, 0xe2, 0x6e, 0xfd, 0x88, 0x8a, 0xf3, 0x79, 0x55, 0xae, 0x88, 0x09, 0x22, 0xf5, 0xdf, 0x61, - 0xa5, 0x3e, 0x0d, 0x4f, 0x2f, 0x6d, 0x1f, 0xd4, 0xe0, 0x69, 0x4f, 0x4e, 0x18, 0xb3, 0x42, 0x21, - 0x5f, 0x31, 0x38, 0x6d, 0x34, 0x5c, 0xa8, 0x31, 0x79, 0xdf, 0xb5, 0x59, 0x3a, 0xc6, 0x98, 0x50, - 0x1a, 0x36, 0xd9, 0xc3, 0x73, 0x2f, 0xd5, 0x7b, 0x37, 0x3b, 0xc0, 0x66, 0xf9, 0xb4, 0xec, 0x7c, - 0x26, 0x48, 0x0b, 0xee, 0x58, 0x9b, 0xdf, 0xd0, 0x87, 0x5e, 0x7f, 0x74, 0x60, 0xfb, 0xa6, 0xe3, - 0x1d, 0x38, 0x55, 0x84, 0x6c, 0x54, 0xb3, 0x73, 0x2d, 0xc6, 0x4b, 0x24, 0xb6, 0x05, 0x34, 0x65, - 0xeb, 0x50, 0xd3, 0x9a, 0x20, 0x98, 0xa2, 0xcd, 0xe7, 0x01, 0x24, 0xcd, 0x94, 0xf9, 0xb7, 0xf9, - 0x11, 0xef, 0xf1, 0x00, 0x53, 0xfd, 0x9d, 0xaf, 0x15, 0x49, 0xc6, 0xa7, 0xf4, 0x16, 0xa7, 0xfd, - 0x00, 0x36, 0x8d, 0x68, 0x8f, 0x5c, 0x69, 0xdb, 0x5c, 0x03, 0x4e, 0x14, 0xcc, 0x58, 0xea, 0xb1, - 0x6f, 0x21, 0x32, 0x39, 0xf8, 0xbd, 0x96, 0x6b, 0x99, 0xf2, 0x49, 0x4c, 0x89, 0xab, 0xc1, 0xe4, - 0x90, 0x37, 0xae, 0x07, 0x6f, 0xe2, 0x25, 0x94, 0xc8, 0x51, 0x25, 0xf1, 0x19, 0x00, 0x18, 0x30, - 0x58, 0x7b, 0x67, 0xc6, 0x13, 0xbf, 0x0f, 0x20, 0xd9, 0x50, 0x4c, 0x38, 0x18, 0x6f, 0x2a, 0x63, - 0x78, 0xc2, 0x45, 0xe7, 0xf2, 0x5d, 0x5a, 0xe6, 0xc2, 0x41, 0x66, 0x13, 0x25, 0xb0, 0x04, 0x17, - 0x76, 0xfc, 0x56, 0xfe, 0x88, 0x8a, 0x01, 0xee, 0x96, 0x39, 0x45, 0x17, 0x72, 0x90, 0x27, 0x14, - 0x59, 0x9a, 0x0c, 0xe1, 0xe5, 0xde, 0xac, 0x6b, 0xbd, 0x92, 0x7f, 0x4a, 0x01, 0x25, 0x17, 0x59, - 0xd8, 0xb3, 0x8b, 0x6f, 0x0f, 0x9a, 0x89, 0xf8, 0x37, 0xc2, 0xd7, 0x96, 0x02, 0x91, 0x8e, 0x6b, - 0xf3, 0xad, 0xd6, 0x6a, 0x73, 0xc3, 0x8d, 0xe5, 0xfa, 0xea, 0xa7, 0x1e, 0x95, 0xd2, 0x9d, 0xec, - 0xe1, 0x73, 0xe7, 0x43, 0xe7, 0x2d, 0x1f, 0xe3, 0x4e, 0x0f, 0xb0, 0x8e, 0x71, 0xec, 0xd0, 0x01, - 0x74, 0xb8, 0x2f, 0x0e, 0x96, 0xca, 0xf0, 0x59, 0x1f, 0xd8, 0x85, 0xa2, 0xa2, 0x6e, 0x42, 0x4a, - 0xb0, 0x17, 0xfd, 0xa2, 0x75, 0x60, 0x5f, 0xb2, 0xdd, 0x2d, 0xa7, 0xa5, 0xcb, 0x20, 0x99, 0xbf, - 0xfb, 0xab, 0xf7, 0x6a, 0x4d, 0x3d, 0xa4, 0x08, 0x12, 0x6f, 0x88, 0x92, 0xf7, 0x54, 0xa7, 0xc5, - 0xea, 0xbd, 0xb5, 0x38, 0x38, 0xf9, 0x2d, 0xbb, 0xb8, 0x3d, 0x4b, 0x78, 0x7c, 0xeb, 0x87, 0x3f, - 0x33, 0x32, 0x4b, 0x7a, 0xfc, 0x9a, 0xcf, 0xfe, 0xef, 0x01, 0x92, 0x38, 0xde, 0xa1, 0xf3, 0xb8, - 0x6e, 0x91, 0xcc, 0x5e, 0x4d, 0x13, 0x34, 0x2a, 0xc2, 0xc9, 0xdb, 0xc7, 0x74, 0x9f, 0x67, 0xe9, - 0xb9, 0x6d, 0x0b, 0x9d, 0x81, 0xa8, 0x58, 0x08, 0xde, 0x94, 0x1a, 0x04, 0xb7, 0xed, 0xb8, 0xf6, - 0x28, 0x8e, 0xb7, 0x06, 0xcf, 0x70, 0x6f, 0xfd, 0xc7, 0xf1, 0xee, 0x9f, 0x6f, 0xb1, 0x04, 0x71, - 0xc5, 0x52, 0x50, 0xbd, 0x47, 0xee, 0xbc, 0xe9, 0x8d, 0xaa, 0xe9, 0xf5, 0x26, 0xda, 0x99, 0x56, - 0x44, 0x3b, 0xd8, 0x3c, 0x04, 0x76, 0x89, 0x52, 0xdf, 0xa1, 0x26, 0x1f, 0xd4, 0xc4, 0x2b, 0xe9, - 0x0d, 0x0c, 0x16, 0x41, 0x66, 0xc1, 0x56, 0xc0, 0x39, 0xa0, 0x18, 0x78, 0xda, 0x1c, 0x4a, 0x8e, - 0xdf, 0x17, 0xe9, 0xf1, 0x8a, 0x72, 0x36, 0x66, 0xef, 0x60, 0x84, 0xa3, 0x34, 0xbc, 0x53, 0x21, - 0xe1, 0x87, 0x0b, 0x2d, 0x26, 0x5f, 0xeb, 0xc0, 0x2e, 0x14, 0xb8, 0x90, 0xbb, 0x2b, 0x22, 0xb6, - 0x8b, 0xde, 0xee, 0x10, 0xe3, 0x4b, 0x7f, 0xcd, 0x20, 0xa1, 0xaf, 0xa5, 0x8f, 0xb7, 0xa9, 0x62, - 0x27, 0xbe, 0x5f, 0x95, 0xed, 0x10, 0xb8, 0x76, 0x89, 0xbd, 0xb2, 0x1f, 0xde, 0x34, 0x94, 0x21, - 0x6b, 0x84, 0x2c, 0x18, 0x8d, 0x4f, 0xf4, 0x4c, 0x0a, 0xef, 0xad, 0xd6, 0x28, 0x32, 0x30, 0x57, - 0xa9, 0xc3, 0xb3, 0xf6, 0xbe, 0x0a, 0x8b, 0x3e, 0x9f, 0x72, 0x49, 0x22, 0x7b, 0x4a, 0xc5, 0xa7, - 0xf2, 0x89, 0x33, 0x83, 0xfa, 0x49, 0x98, 0x27, 0x7f, 0xa2, 0x4f, 0xce, 0xd1, 0xb3, 0x10, 0xfd, - 0xb6, 0x3c, 0x16, 0x43, 0x83, 0x0e, 0x9e, 0xde, 0xd2, 0x0f, 0x35, 0xa5, 0xc5, 0x61, 0x80, 0x6e, - 0x3f, 0xc4, 0xec, 0x35, 0x4a, 0x23, 0xe1, 0x4d, 0xed, 0x6b, 0x4b, 0x8a, 0xf6, 0xad, 0x2f, 0x4b, - 0xfe, 0x21, 0x55, 0xb9, 0xf7, 0xa5, 0x07, 0x1b, 0xa3, 0x34, 0xd2, 0x7c, 0x42, 0xe8, 0x78, 0x20, - 0xab, 0xe1, 0xa2, 0x34, 0x76, 0xfc, 0x4e, 0x80, 0x00, 0x52, 0xea, 0x09, 0xf0, 0xc3, 0x4e, 0xc1, - 0x16, 0x35, 0x38, 0x11, 0x1a, 0x1f, 0xaf, 0xfd, 0x50, 0x13, 0x4c, 0x10, 0xa9, 0x3a, 0x47, 0x00, - 0x96, 0x68, 0x2d, 0x68, 0xae, 0xb7, 0xe5, 0x0b, 0xba, 0x92, 0x37, 0x40, 0x8c, 0xf0, 0xf3, 0xc9, - 0xa6, 0x1c, 0xf3, 0x18, 0x96, 0x7d, 0xf0, 0xe6, 0xde, 0xcd, 0xb0, 0xef, 0x3e, 0x03, 0xc4, 0xf0, - 0x38, 0xf3, 0xfc, 0x4f, 0xd2, 0xa9, 0x33, 0xae, 0x6c, 0x48, 0x40, 0x37, 0x87, 0xa2, 0x9f, 0x03, - 0x93, 0xed, 0x7f, 0x96, 0x11, 0x67, 0x11, 0x7d, 0xb5, 0xb6, 0x84, 0x20, 0xb0, 0x04, 0x30, 0xfb, - 0xb9, 0x88, 0x3c, 0x17, 0x45, 0xf1, 0xac, 0x27, 0xad, 0x52, 0xe0, 0x0a, 0x97, 0xdc, 0x33, 0xd5, - 0x3f, 0x7a, 0x10, 0x29, 0xe5, 0x40, 0x1c, 0xf8, 0xde, 0x66, 0x6c, 0x04, 0x6b, 0x97, 0x3e, 0x20, - 0xbf, 0x59, 0x04, 0xd7, 0x12, 0xa2, 0xcc, 0x99, 0x3a, 0xf1, 0x5c, 0xc2, 0x1c, 0x9a, 0x0b, 0x03, - 0x0b, 0x9a, 0x61, 0x9d, 0xf2, 0xac, 0x85, 0x68, 0xd4, 0xd0, 0x79, 0x95, 0xa3, 0x82, 0x5e, 0x95, - 0x8c, 0xb4, 0x09, 0x45, 0xe4, 0x12, 0x17, 0x54, 0xb9, 0xf2, 0xaa, 0x82, 0xf7, 0xf7, 0x49, 0x95, - 0x28, 0xa8, 0xa8, 0xf9, 0x98, 0x07, 0x4f, 0x32, 0xeb, 0x96, 0xcb, 0x60, 0x9d, 0x82, 0x7c, 0x78, - 0x23, 0xa5, 0xc1, 0x94, 0x6b, 0x84, 0x64, 0x81, 0x89, 0xdd, 0xef, 0x66, 0xfc, 0x8f, 0x62, 0x16, - 0xc8, 0xe0, 0x17, 0xe3, 0x00, 0x85, 0x46, 0xb9, 0xad, 0xd9, 0x74, 0xce, 0x03, 0x10, 0xad, 0x89, - 0x85, 0xb0, 0x80, 0x86, 0xfe, 0x0e, 0x8d, 0x5b, 0x3e, 0x97, 0x7c, 0xec, 0x3b, 0xe7, 0xdb, 0x54, - 0xb3, 0xc5, 0xb7, 0xd2, 0x79, 0x87, 0x74, 0xa5, 0x59, 0x86, 0xc4, 0xb2, 0x1d, 0x9a, 0x91, 0x60, - 0x99, 0x5d, 0x01, 0xf1, 0x23, 0x5b, 0x72, 0xe0, 0xf8, 0x1e, 0x51, 0xf5, 0x2b, 0xb3, 0xe0, 0x07, - 0xee, 0x1d, 0x0a, 0xd2, 0x46, 0xe4, 0x3b, 0x29, 0x30, 0x9a, 0x26, 0xe9, 0x4d, 0x4c, 0x8f, 0x93, - 0x76, 0x16, 0xfe, 0xb7, 0x41, 0x5f, 0x10, 0xb0, 0xf3, 0x9e, 0x08, 0xc6, 0x35, 0xd1, 0xef, 0xaa, - 0x76, 0x2a, 0x30, 0xe5, 0x15, 0xa6, 0x06, 0x90, 0xd1, 0x88, 0x6b, 0x7a, 0xc6, 0xe4, 0xde, 0x44, - 0xeb, 0x41, 0x79, 0xe8, 0x74, 0x84, 0xbb, 0x7d, 0x01, 0x7b, 0x95, 0xfc, 0x05, 0x8a, 0x4a, 0xe1, - 0x75, 0x7f, 0x47, 0xed, 0x7d, 0xcf, 0x43, 0x35, 0x6b, 0x41, 0xb2, 0xbc, 0x21, 0x3a, 0xc2, 0x58, - 0x92, 0x1f, 0x85, 0x1d, 0xf4, 0x47, 0xb6, 0x72, 0x58, 0xc7, 0x28, 0xa6, 0xf1, 0x39, 0x65, 0x3e, - 0x5c, 0xea, 0x06, 0x8e, 0x64, 0x3b, 0xf9, 0x44, 0x02, 0xf5, 0xa3, 0xb0, 0xe8, 0x95, 0xf3, 0xbe, - 0x89, 0x29, 0x33, 0xba, 0x24, 0xa9, 0x08, 0x27, 0x34, 0x14, 0xfe, 0xd9, 0x36, 0x4c, 0x06, 0xce, - 0xef, 0xb0, 0x21, 0xfd, 0x57, 0x86, 0xaf, 0x25, 0x9e, 0xfc, 0xd5, 0xd6, 0x85, 0x3d, 0x4b, 0x80, - 0xc0, 0xbd, 0xfb, 0xc2, 0x03, 0xd8, 0xca, 0x70, 0xa4, 0xab, 0x52, 0x5c, 0xfb, 0x69, 0x72, 0x2b, - 0x0f, 0xf2, 0xc4, 0x6f, 0x19, 0x72, 0x4c, 0x95, 0x04, 0x94, 0x76, 0xaf, 0x1b, 0xe9, 0x10, 0x4b, - 0xe9, 0x49, 0xc9, 0x6b, 0x30, 0x83, 0x19, 0x31, 0xae, 0xe8, 0x52, 0xa5, 0x03, 0x2d, 0x9d, 0x6b, - 0x7e, 0x89, 0x5b, 0x62, 0x14, 0x32, 0xd4, 0xdb, 0x00, 0x73, 0x22, 0x69, 0x47, 0xa2, 0xcd, 0x17, - 0x7e, 0x48, 0x8e, 0x47, 0xc6, 0x74, 0x48, 0xf4, 0xb9, 0x9a, 0x1b, 0x67, 0x6a, 0xb0, 0xb2, 0xda, - 0x05, 0x74, 0x73, 0x7a, 0x92, 0x6f, 0xaf, 0xe9, 0xc3, 0x41, 0x41, 0xea, 0x20, 0xef, 0x1e, 0xbf, - 0x84, 0x9e, 0x84, 0xb4, 0x69, 0x69, 0x00, 0x76, 0xb5, 0xf7, 0xa9, 0x99, 0x69, 0x06, 0x7e, 0x89, - 0xa8, 0x70, 0x15, 0x3e, 0xf2, 0x78, 0xab, 0xd3, 0x57, 0x29, 0x49, 0x85, 0x10, 0xc7, 0x5b, 0x11, - 0x67, 0x90, 0x14, 0x57, 0x0b, 0xa6, 0xe5, 0xd2, 0xb5, 0x9e, 0x13, 0xda, 0x33, 0x4f, 0x4c, 0x42, - 0x28, 0x95, 0xc2, 0xd7, 0x20, 0xb4, 0xff, 0x68, 0xe2, 0x4a, 0xfe, 0xe8, 0x10, 0xa0, 0x21, 0x25, - 0x89, 0x4b, 0x75, 0xc5, 0xaa, 0xfc, 0x96, 0x16, 0xe4, 0xfb, 0xe3, 0x56, 0x14, 0xc0, 0x30, 0xab, - 0x18, 0x60, 0x72, 0x2d, 0x36, 0xaf, 0x18, 0x07, 0x76, 0x89, 0x7d, 0xe4, 0x07, 0xeb, 0x3d, 0x13, - 0xd1, 0xe8, 0x5a, 0x18, 0xd2, 0xa7, 0x42, 0x66, 0x91, 0x22, 0x58, 0x39, 0xe1, 0x67, 0x97, 0x95, - 0x81, 0x0a, 0x64, 0xdb, 0x6e, 0x92, 0x13, 0x2a, 0xe6, 0x35, 0x53, 0x18, 0x52, 0x4f, 0xc0, 0x38, - 0x43, 0x30, 0x04, 0x34, 0x03, 0x06, 0xa6, 0x90, 0xcf, 0xd4, 0x0f, 0xdb, 0xd9, 0x41, 0xeb, 0xd2, - 0x54, 0x21, 0x88, 0x3b, 0x08, 0xd9, 0xe1, 0xcc, 0x87, 0xa3, 0x6d, 0x31, 0x85, 0x05, 0xf4, 0x82, - 0x70, 0x49, 0x7f, 0x2b, 0xe3, 0xe8, 0x40, 0xf3, 0xa4, 0x48, 0x5e, 0x4a, 0x8d, 0x91, 0xa9, 0x5e, - 0x08, 0x6b, 0xd4, 0x63, 0x45, 0x8f, 0x78, 0xe0, 0x7f, 0xcb, 0x61, 0x81, 0x50, 0xc0, 0x83, 0xc3, - 0x7f, 0x72, 0x2f, 0x15, 0x5e, 0x0c, 0x29, 0x9f, 0x7f, 0xb8, 0xf5, 0x66, 0x08, 0xb7, 0xb4, 0xee, - 0x9c, 0xd6, 0xac, 0x83, 0x4f, 0x46, 0xf0, 0x0d, 0x3e, 0x4a, 0x2b, 0x4d, 0x55, 0xbb, 0xc7, 0x71, - 0xf3, 0x6a, 0x0f, 0x40, 0xb3, 0xf5, 0xd7, 0xfb, 0x26, 0x2a, 0x73, 0xe4, 0xac, 0x31, 0x63, 0xe4, - 0xd3, 0xef, 0xed, 0xed, 0x48, 0xc5, 0xe6, 0xdb, 0xf3, 0x5e, 0x7c, 0x00, 0x57, 0x17, 0xc7, 0xab, - 0x9f, 0xe2, 0x52, 0x4b, 0xfd, 0x1a, 0x37, 0x53, 0x27, 0xe8, 0x4b, 0x97, 0xbe, 0xb4, 0xc2, 0x79, - 0x2e, 0x6a, 0x10, 0x87, 0xbb, 0x3c, 0x2c, 0xd4, 0xbc, 0xda, 0x70, 0xe1, 0xf3, 0x51, 0x96, 0xfe, - 0xfd, 0xd3, 0x28, 0x20, 0xe3, 0x79, 0x7d, 0x92, 0x70, 0x7d, 0x10, 0x14, 0x57, 0x14, 0x43, 0x46, - 0x2b, 0x09, 0x15, 0x9b, 0x28, 0x2f, 0x67, 0xbf, 0xc7, 0xf4, 0xad, 0x77, 0xd0, 0xb7, 0xca, 0xc3, - 0x5c, 0x79, 0x84, 0xb5, 0x4f, 0x20, 0x6b, 0xdb, 0x3d, 0x73, 0x81, 0x0e, 0x64, 0xbd, 0x44, 0x86, - 0xf0, 0xc6, 0xce, 0xf5, 0x4b, 0xbc, 0x10, 0x7b, 0xee, 0x97, 0x48, 0xf2, 0x48, 0x95, 0xd8, 0x17, - 0xda, 0xd9, 0x78, 0x72, 0x39, 0x54, 0x8f, 0x72, 0x49, 0xca, 0x03, 0x33, 0x8d, 0xe0, 0xaa, 0x9c, - 0xca, 0x53, 0xff, 0x34, 0x78, 0xba, 0x10, 0x76, 0x32, 0x3b, 0xcd, 0xd1, 0x2b, 0x17, 0x26, 0x41, - 0x16, 0x1b, 0xba, 0xfa, 0x84, 0xd9, 0x2a, 0x81, 0xd0, 0x73, 0x7d, 0x02, 0x32, 0x24, 0xea, 0xd2, - 0x9a, 0xa6, 0xa5, 0x76, 0x48, 0x9a, 0xd8, 0xf7, 0x58, 0xa5, 0x3c, 0x95, 0xc8, 0x1f, 0x23, 0x3e, - 0xef, 0x0b, 0xff, 0x75, 0x28, 0xcf, 0x7f, 0xea, 0xc3, 0x37, 0x0a, 0xe8, 0xb2, 0x85, 0x26, 0x3a, - 0x8c, 0x9c, 0x40, 0x18, 0x55, 0xeb, 0x65, 0x06, 0x9e, 0x65, 0x32, 0x42, 0x5c, 0x03, 0x1a, 0x2d, - 0x0d, 0x18, 0xd9, 0x08, 0xcc, 0x6a, 0x96, 0xc3, 0x0f, 0x17, 0x0a, 0x00, 0x74, 0x00, 0x7c, 0xf7, - 0x6a, 0x48, 0x81, 0xd0, 0xcc, 0xb9, 0xf0, 0x5f, 0x70, 0xd5, 0xe6, 0x53, 0x67, 0x82, 0x75, 0x61, - 0x0d, 0x5e, 0xbc, 0x7d, 0xa1, 0xa0, 0xd0, 0x59, 0xc5, 0xb6, 0x97, 0x4c, 0xd8, 0xdf, 0x21, 0x07, - 0xcd, 0x9b, 0x8d, 0xb8, 0xdf, 0x40, 0x98, 0xe2, 0x32, 0xbf, 0x5e, 0x32, 0xb4, 0xbf, 0xd3, 0x5b, - 0x3b, 0x7d, 0x1e, 0x3e, 0x66, 0x89, 0x51, 0xc8, 0xc9, 0xee, 0xa1, 0xc7, 0x62, 0x68, 0x79, 0x65, - 0x24, 0x2d, 0xc1, 0x6b, 0xde, 0x2b, 0x01, 0xec, 0x26, 0xfa, 0x99, 0xb3, 0x6e, 0x6c, 0xda, 0x24, - 0xb3, 0x69, 0x80, 0x3f, 0xf2, 0xfe, 0xac, 0xad, 0x9e, 0x95, 0xf8, 0xe3, 0xa7, 0x34, 0x6f, 0xa4, - 0xff, 0x14, 0x53, 0xe0, 0x3a, 0xfd, 0x47, 0xa8, 0x2e, 0x79, 0x28, 0x0b, 0xb5, 0x10, 0xc7, 0xca, - 0x70, 0x6f, 0x28, 0x27, 0xed, 0x84, 0x70, 0x71, 0x80, 0x8b, 0x85, 0xe6, 0xdd, 0xf4, 0xc8, 0x41, - 0x18, 0xee, 0xff, 0xc3, 0x30, 0x3b, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, - 0x1a, 0x04, 0x14, 0x33, 0x17, 0x2b, 0x61, 0x77, 0xe7, 0xca, 0x9d, 0x35, 0xbb, 0xcc, 0x11, 0xce, - 0xb3, 0x3c, 0x8c, 0xcb, 0x38, 0xd6, 0xfc, 0x04, 0x14, 0x74, 0x81, 0x8c, 0x45, 0x9e, 0x8e, 0xd2, - 0x42, 0x5f, 0x6b, 0xae, 0x1a, 0x5d, 0x8f, 0x73, 0x1f, 0xc3, 0xbd, 0x8d, 0x8f, 0x02, 0x02, 0x07, - 0xd0 }; - - public static X509Certificate2 Certificate { get; } = new X509Certificate2(kdcPfxData); -} \ No newline at end of file diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs index 4dfeee5a512acd..36927da21cbabf 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs @@ -50,7 +50,7 @@ public FakePrincipalService(string realm) public X509Certificate2 RetrieveKdcCertificate() { - return FakeKdcCertificate.Certificate; + throw new NotImplementedException(); } private static readonly Dictionary KeyCache = new(); diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs index e6ee0c34d10e3f..267e9e4982072a 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs @@ -10,10 +10,10 @@ class FakeRealmService : IRealmService { private readonly KerberosCompatibilityFlags compatibilityFlags; - public FakeRealmService(string realm, Krb5Config? config = null, KerberosCompatibilityFlags compatibilityFlags = KerberosCompatibilityFlags.None) + public FakeRealmService(string realm, Krb5Config config, KerberosCompatibilityFlags compatibilityFlags = KerberosCompatibilityFlags.None) { this.Name = realm; - this.Configuration = config ?? Krb5Config.Kdc(); + this.Configuration = config; this.compatibilityFlags = compatibilityFlags; } diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs index 29ea22c79400fd..81d1e181d366e2 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs @@ -9,6 +9,7 @@ using System.Net.Security; using System.Runtime.InteropServices; using System.Threading.Tasks; +using Kerberos.NET.Configuration; using Kerberos.NET.Crypto; using Kerberos.NET.Entities; using Kerberos.NET.Server; @@ -31,10 +32,14 @@ public class KerberosExecutor : IDisposable public KerberosExecutor(string realm) { + var krb5Config = Krb5Config.Default(); + krb5Config.KdcDefaults.RegisterDefaultPkInitPreAuthHandler = false; + _options = new ListenerOptions { + Configuration = krb5Config, DefaultRealm = realm, - RealmLocator = realm => new FakeRealmService(realm) + RealmLocator = realm => new FakeRealmService(realm, krb5Config) }; _kdcListener = new FakeKdcServer(_options); diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems b/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems index c5b4a144189e57..6371a6a6b6fb70 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/System.Net.Security.Kerberos.Shared.projitems @@ -10,9 +10,6 @@ - - CommonTest\System\Net\Security\Kerberos\FakeKdcCertificate.cs - CommonTest\System\Net\Security\Kerberos\FakeKdcServer.cs From e928da5c7453a5fbb072176629efd6f981dda339 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 11 Jul 2022 21:05:55 +0000 Subject: [PATCH 05/11] Wire up KDC logging to Xunit test output --- .../Net/Security/Kerberos/KerberosExecutor.cs | 16 +++++++++++----- .../NegotiateAuthenticationKerberosTest.cs | 17 +++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs index 81d1e181d366e2..556e0facd8eeb9 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs @@ -5,14 +5,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; -using System.Net.Security; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Kerberos.NET.Configuration; using Kerberos.NET.Crypto; using Kerberos.NET.Entities; using Kerberos.NET.Server; +using Kerberos.NET.Logging; +using Xunit.Abstractions; namespace System.Net.Security.Kerberos; @@ -30,16 +29,23 @@ public class KerberosExecutor : IDisposable public static string FakePassword { get; } = "P@ssw0rd!"; - public KerberosExecutor(string realm) + public KerberosExecutor(ITestOutputHelper testOutputHelper, string realm) { var krb5Config = Krb5Config.Default(); krb5Config.KdcDefaults.RegisterDefaultPkInitPreAuthHandler = false; + var logger = new KerberosDelegateLogger( + (level, categoryName, eventId, scopeState, logState, exception, log) => + testOutputHelper.WriteLine($"[{level}] [{categoryName}] {log}") + ); + _options = new ListenerOptions { Configuration = krb5Config, DefaultRealm = realm, - RealmLocator = realm => new FakeRealmService(realm, krb5Config) + RealmLocator = realm => new FakeRealmService(realm, krb5Config), + Log = logger, + IsDebug = true, }; _kdcListener = new FakeKdcServer(_options); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs index e6c4e5d394d52d..ca10f8e15e23cd 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs @@ -1,26 +1,27 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Buffers; -using System.Buffers.Binary; -using System.IO; -using System.Net.Security; -using System.Text; using System.Threading.Tasks; -using System.Net.Test.Common; using System.Net.Security.Kerberos; using Xunit; +using Xunit.Abstractions; namespace System.Net.Security.Tests { [ConditionalClass(typeof(KerberosExecutor), nameof(KerberosExecutor.IsSupported))] public class NegotiateAuthenticationKerberosTest { + private readonly ITestOutputHelper _testOutputHelper; + + public NegotiateAuthenticationKerberosTest(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + [Fact] public async Task Loopback_Kerberos_Authentication() { - using var kerberosExecutor = new KerberosExecutor("LINUX.CONTOSO.COM"); + using var kerberosExecutor = new KerberosExecutor(_testOutputHelper, "LINUX.CONTOSO.COM"); kerberosExecutor.AddService("HTTP/linux.contoso.com"); From c6619e191e51a6d1400ff1237a971a6277db13ce Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 12 Jul 2022 09:06:50 +0000 Subject: [PATCH 06/11] Add support for specifying Kerberos package on Linux/macOS (in addition to NTLM and Negotiate) --- .../Interop.NetSecurityNative.PackageType.cs | 17 ++++++ .../Interop.NetSecurityNative.cs | 14 ++--- .../Win32/SafeHandles/GssSafeHandles.cs | 6 +- .../Net/Security/NegotiateStreamPal.Unix.cs | 48 ++++++++++----- .../Security/Unix/SafeFreeNegoCredentials.cs | 14 ++--- .../src/System.Net.Security.csproj | 2 + .../System.Net.Security.Unit.Tests.csproj | 2 + .../System.Net.Security.Native/pal_gssapi.c | 59 +++++++++++-------- .../System.Net.Security.Native/pal_gssapi.h | 13 +++- 9 files changed, 117 insertions(+), 58 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs new file mode 100644 index 00000000000000..d8d6073c3d04a7 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +internal static partial class Interop +{ + internal static partial class NetSecurityNative + { + internal enum PackageType : uint + { + Negotiate = 0, + NTLM = 1, + Kerberos = 2, + } + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs index 3ce98eb5d30769..84a31968d79378 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs @@ -61,7 +61,7 @@ internal static partial Status InitiateCredSpNego( [LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredWithPassword", StringMarshalling = StringMarshalling.Utf8)] internal static partial Status InitiateCredWithPassword( out Status minorStatus, - [MarshalAs(UnmanagedType.Bool)] bool isNtlm, + PackageType packageType, SafeGssNameHandle desiredName, string password, int passwordLen, @@ -77,7 +77,7 @@ private static partial Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - [MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly, + PackageType packageType, SafeGssNameHandle? targetName, uint reqFlags, ref byte inputBytes, @@ -91,7 +91,7 @@ private static partial Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - [MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly, + PackageType packageType, IntPtr cbt, int cbtSize, SafeGssNameHandle? targetName, @@ -106,7 +106,7 @@ internal static Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - bool isNtlmOnly, + PackageType packageType, SafeGssNameHandle? targetName, uint reqFlags, ReadOnlySpan inputBytes, @@ -118,7 +118,7 @@ internal static Status InitSecContext( out minorStatus, initiatorCredHandle, ref contextHandle, - isNtlmOnly, + packageType, targetName, reqFlags, ref MemoryMarshal.GetReference(inputBytes), @@ -132,7 +132,7 @@ internal static Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - bool isNtlmOnly, + PackageType packageType, IntPtr cbt, int cbtSize, SafeGssNameHandle? targetName, @@ -146,7 +146,7 @@ internal static Status InitSecContext( out minorStatus, initiatorCredHandle, ref contextHandle, - isNtlmOnly, + packageType, cbt, cbtSize, targetName, diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs index 253e0548dea9d9..acc95913c863c5 100644 --- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs +++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs @@ -91,9 +91,9 @@ public static SafeGssCredHandle CreateAcceptor() /// returns the handle for the given credentials. /// The method returns an invalid handle if the username is null or empty. /// - public static SafeGssCredHandle Create(string username, string password, bool isNtlmOnly) + public static SafeGssCredHandle Create(string username, string password, Interop.NetSecurityNative.PackageType packageType) { - if (isNtlmOnly && !s_IsNtlmInstalled.Value) + if (packageType == Interop.NetSecurityNative.PackageType.NTLM && !s_IsNtlmInstalled.Value) { throw new Interop.NetSecurityNative.GssApiException( Interop.NetSecurityNative.Status.GSS_S_BAD_MECH, @@ -117,7 +117,7 @@ public static SafeGssCredHandle Create(string username, string password, bool is } else { - status = Interop.NetSecurityNative.InitiateCredWithPassword(out minorStatus, isNtlmOnly, userHandle, password, Encoding.UTF8.GetByteCount(password), out retHandle); + status = Interop.NetSecurityNative.InitiateCredWithPassword(out minorStatus, packageType, userHandle, password, Encoding.UTF8.GetByteCount(password), out retHandle); } if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 73043391901aef..702167758da69c 100644 --- a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -134,7 +134,7 @@ private static SecurityStatusPal EstablishSecurityContext( out byte[]? resultBuffer, ref ContextFlagsPal outFlags) { - bool isNtlmOnly = credential.IsNtlmOnly; + Interop.NetSecurityNative.PackageType packageType = credential.PackageType; resultBuffer = null; @@ -142,7 +142,11 @@ private static SecurityStatusPal EstablishSecurityContext( { if (NetEventSource.Log.IsEnabled()) { - string protocol = isNtlmOnly ? "NTLM" : "SPNEGO"; + string protocol = packageType switch { + Interop.NetSecurityNative.PackageType.NTLM => "NTLM", + Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos", + _ => "SPNEGO" + }; NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}"); } @@ -172,7 +176,7 @@ private static SecurityStatusPal EstablishSecurityContext( status = Interop.NetSecurityNative.InitSecContext(out minorStatus, credential.GssCredential, ref contextHandle, - isNtlmOnly, + packageType, cbtAppData, cbtAppDataSize, negoContext.TargetName, @@ -187,7 +191,7 @@ private static SecurityStatusPal EstablishSecurityContext( status = Interop.NetSecurityNative.InitSecContext(out minorStatus, credential.GssCredential, ref contextHandle, - isNtlmOnly, + packageType, negoContext.TargetName, (uint)inputFlags, incomingBlob, @@ -216,7 +220,11 @@ private static SecurityStatusPal EstablishSecurityContext( { if (NetEventSource.Log.IsEnabled()) { - string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"; + string protocol = packageType switch { + Interop.NetSecurityNative.PackageType.NTLM => "NTLM", + Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos", + _ => isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos" + }; NetEventSource.Info(context, $"actual protocol = {protocol}"); } @@ -441,24 +449,36 @@ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, boo { bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) || string.IsNullOrWhiteSpace(credential.Password); - bool ntlmOnly = string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase); - if (ntlmOnly && isEmptyCredential && !isServer) + Interop.NetSecurityNative.PackageType packageType; + + if (string.Equals(package, NegotiationInfoClass.Negotiate, StringComparison.OrdinalIgnoreCase)) { - // NTLM authentication is not possible with default credentials which are no-op - throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred); + packageType = Interop.NetSecurityNative.PackageType.Negotiate; } - - if (!ntlmOnly && !string.Equals(package, NegotiationInfoClass.Negotiate)) + else if (string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase)) + { + packageType = Interop.NetSecurityNative.PackageType.NTLM; + if (isEmptyCredential && !isServer) + { + // NTLM authentication is not possible with default credentials which are no-op + throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred); + } + } + else if (string.Equals(package, NegotiationInfoClass.Kerberos, StringComparison.OrdinalIgnoreCase)) + { + packageType = Interop.NetSecurityNative.PackageType.Kerberos; + } + else { - // Native shim currently supports only NTLM and Negotiate + // Native shim currently supports only NTLM, Negotiate and Kerberos throw new PlatformNotSupportedException(SR.net_securitypackagesupport); } try { return isEmptyCredential ? - new SafeFreeNegoCredentials(ntlmOnly, string.Empty, string.Empty, string.Empty) : - new SafeFreeNegoCredentials(ntlmOnly, credential.UserName, credential.Password, credential.Domain); + new SafeFreeNegoCredentials(packageType, string.Empty, string.Empty, string.Empty) : + new SafeFreeNegoCredentials(packageType, credential.UserName, credential.Password, credential.Domain); } catch (Exception ex) { diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs index 58d1942829e524..ffbd46db32aaf4 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs @@ -12,7 +12,7 @@ namespace System.Net.Security internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials { private SafeGssCredHandle _credential; - private readonly bool _isNtlmOnly; + private readonly Interop.NetSecurityNative.PackageType _packageType; private readonly string _userName; private readonly bool _isDefault; @@ -21,10 +21,10 @@ public SafeGssCredHandle GssCredential get { return _credential; } } - // Property represents if Ntlm Protocol is specfied or not. - public bool IsNtlmOnly + // Property represents which protocol is specfied (Negotiate, Ntlm or Kerberos). + public Interop.NetSecurityNative.PackageType PackageType { - get { return _isNtlmOnly; } + get { return _packageType; } } public string UserName @@ -37,7 +37,7 @@ public bool IsDefault get { return _isDefault; } } - public SafeFreeNegoCredentials(bool isNtlmOnly, string username, string password, string domain) + public SafeFreeNegoCredentials(Interop.NetSecurityNative.PackageType packageType, string username, string password, string domain) : base(IntPtr.Zero, true) { Debug.Assert(username != null && password != null, "Username and Password can not be null"); @@ -66,10 +66,10 @@ public SafeFreeNegoCredentials(bool isNtlmOnly, string username, string password } bool ignore = false; - _isNtlmOnly = isNtlmOnly; + _packageType = packageType; _userName = username; _isDefault = string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password); - _credential = SafeGssCredHandle.Create(username, password, isNtlmOnly); + _credential = SafeGssCredHandle.Create(username, password, packageType); _credential.DangerousAddRef(ref ignore); } diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 139ea7ce4ba064..55d140a914f553 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -252,6 +252,8 @@ Link="Common\Interop\Unix\Interop.Errors.cs" /> + + Date: Tue, 12 Jul 2022 09:08:10 +0000 Subject: [PATCH 07/11] Cleanup the fake KDC implementation, make user creation explicit, add test for invalid credentials --- .../Net/Security/Kerberos/FakeKdcServer.cs | 48 +++---- .../Kerberos/FakeKerberosPrincipal.cs | 135 ++---------------- .../Security/Kerberos/FakePrincipalService.cs | 31 ++-- .../Security/Kerberos/FakeRealmReferral.cs | 8 +- .../Net/Security/Kerberos/FakeRealmService.cs | 20 +-- .../Security/Kerberos/FakeRealmSettings.cs | 6 +- .../Security/Kerberos/FakeTrustedRealms.cs | 6 +- .../Net/Security/Kerberos/KerberosExecutor.cs | 58 +++++--- .../NegotiateAuthenticationKerberosTest.cs | 26 +++- 9 files changed, 131 insertions(+), 207 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs index 27796a22b34be6..cc4414bb69724b 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKdcServer.cs @@ -13,33 +13,33 @@ namespace System.Net.Security.Kerberos; class FakeKdcServer { - private readonly KdcServer kdcServer; - private readonly TcpListener tcpListener; - private CancellationTokenSource? cancellationTokenSource; - private bool running; - private readonly object runningLock; + private readonly KdcServer _kdcServer; + private readonly TcpListener _tcpListener; + private CancellationTokenSource? _cancellationTokenSource; + private bool _running; + private readonly object _runningLock; public FakeKdcServer(KdcServerOptions serverOptions) { - kdcServer = new KdcServer(serverOptions); - tcpListener = new TcpListener(System.Net.IPAddress.Loopback, 0); - runningLock = new object(); + _kdcServer = new KdcServer(serverOptions); + _tcpListener = new TcpListener(System.Net.IPAddress.Loopback, 0); + _runningLock = new object(); } public Task Start() { - cancellationTokenSource = new CancellationTokenSource(); - running = true; - tcpListener.Start(); + _cancellationTokenSource = new CancellationTokenSource(); + _running = true; + _tcpListener.Start(); - var cancellationToken = cancellationTokenSource.Token; + var cancellationToken = _cancellationTokenSource.Token; Task.Run(async () => { try { byte[] sizeBuffer = new byte[4]; do { - using var socket = await tcpListener.AcceptSocketAsync(cancellationToken); + using var socket = await _tcpListener.AcceptSocketAsync(cancellationToken); using var socketStream = new NetworkStream(socket); await socketStream.ReadExactlyAsync(sizeBuffer, cancellationToken); @@ -47,7 +47,7 @@ public Task Start() var requestRented = ArrayPool.Shared.Rent(messageSize); var request = requestRented.AsMemory(0, messageSize); await socketStream.ReadExactlyAsync(request); - var response = await kdcServer.ProcessMessage(request); + var response = await _kdcServer.ProcessMessage(request); ArrayPool.Shared.Return(requestRented); var responseLength = response.Length + 4; var responseRented = ArrayPool.Shared.Rent(responseLength); @@ -60,29 +60,29 @@ public Task Start() } finally { - lock (runningLock) + lock (_runningLock) { - running = false; - Monitor.Pulse(runningLock); + _running = false; + Monitor.Pulse(_runningLock); } } }); - return Task.FromResult((IPEndPoint)tcpListener.LocalEndpoint); + return Task.FromResult((IPEndPoint)_tcpListener.LocalEndpoint); } public void Stop() { - if (running) + if (_running) { - cancellationTokenSource?.Cancel(); - lock (runningLock) + _cancellationTokenSource?.Cancel(); + lock (_runningLock) { - while (running) + while (_running) { - Monitor.Wait(runningLock); + Monitor.Wait(_runningLock); } } - tcpListener.Stop(); + _tcpListener.Stop(); } } } diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs index 01e0b72eb22d06..0fe771efadda53 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeKerberosPrincipal.cs @@ -4,39 +4,23 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; -using System.Text; using Kerberos.NET.Crypto; using Kerberos.NET.Entities; -using Kerberos.NET.Entities.Pac; using Kerberos.NET.Server; namespace System.Net.Security.Kerberos; class FakeKerberosPrincipal : IKerberosPrincipal { - private static readonly byte[] KrbTgtKey = new byte[] - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }; - - private static readonly SecurityIdentifier DomainSid = new( - IdentifierAuthority.NTAuthority, - new uint[] { 123, 456, 789, 012, 321 }, - 0 - ); - - private readonly SecurityIdentifier userSid = new(DomainSid, 888); + private readonly byte[] _password; - private readonly SecurityIdentifier groupSid = new(DomainSid, 513); - - internal static readonly byte[] FakePassword = Encoding.Unicode.GetBytes("P@ssw0rd!"); - - public FakeKerberosPrincipal(string principalName, string realm) + public FakeKerberosPrincipal(PrincipalType type, string principalName, string realm, byte[] password) { + this.Type = type; this.PrincipalName = principalName; this.Realm = realm; this.Expires = DateTimeOffset.UtcNow.AddMonths(9999); + this._password = password; } public SupportedEncryptionTypes SupportedEncryptionTypes { get; set; } @@ -54,28 +38,7 @@ public FakeKerberosPrincipal(string principalName, string realm) PaDataType.PA_PK_AS_REQ }; - public PrincipalType Type - { - get - { - if (this.PrincipalName == "krbtgt" || this.PrincipalName.Equals($"krbtgt/{Realm}", StringComparison.CurrentCultureIgnoreCase)) - { - return PrincipalType.Service; - } - - if (this.PrincipalName.StartsWith("krbtgt/", StringComparison.InvariantCultureIgnoreCase)) - { - return PrincipalType.TrustedDomain; - } - - if (this.PrincipalName.Contains('/')) - { - return PrincipalType.Service; - } - - return PrincipalType.User; - } - } + public PrincipalType Type { get; private set; } public string PrincipalName { get; private set; } @@ -83,95 +46,25 @@ public PrincipalType Type public DateTimeOffset? Expires { get; set; } - public PrivilegedAttributeCertificate GeneratePac() - { - var pac = new PrivilegedAttributeCertificate() - { - LogonInfo = new PacLogonInfo - { - DomainName = Realm, - UserName = PrincipalName, - UserDisplayName = PrincipalName, - BadPasswordCount = 12, - SubAuthStatus = 0, - DomainSid = DomainSid, - UserSid = userSid, - GroupSid = groupSid, - LogonTime = DateTimeOffset.UtcNow, - ServerName = "server", - UserAccountControl = UserAccountControlFlags.ADS_UF_NORMAL_ACCOUNT, - UserFlags = UserFlags.LOGON_WINLOGON, - - } - }; - - return pac; - } - - private KerberosKey? TgtKey { get; set; } + public PrivilegedAttributeCertificate? GeneratePac() => null; private static readonly ConcurrentDictionary KeyCache = new(); public KerberosKey RetrieveLongTermCredential() { - EncryptionType etype = ExtractEType(this.PrincipalName); - - return this.RetrieveLongTermCredential(etype); + return this.RetrieveLongTermCredential(EncryptionType.AES256_CTS_HMAC_SHA1_96); } public KerberosKey RetrieveLongTermCredential(EncryptionType etype) { - KerberosKey key; - - if (this.PrincipalName.StartsWith("krbtgt", StringComparison.InvariantCultureIgnoreCase)) + return KeyCache.GetOrAdd(etype + this.PrincipalName, pn => { - key = this.TgtKey ?? (this.TgtKey = new( - password: KrbTgtKey, - principal: new PrincipalName(PrincipalNameType.NT_PRINCIPAL, Realm, new[] { "krbtgt" }), - etype: EncryptionType.AES256_CTS_HMAC_SHA1_96, - saltType: SaltType.ActiveDirectoryUser - )); - } - else - { - key = KeyCache.GetOrAdd(etype + this.PrincipalName, pn => - { - return new KerberosKey( - password: FakePassword, - principal: new PrincipalName(PrincipalNameType.NT_PRINCIPAL, Realm, new[] { this.PrincipalName }), - etype: etype, - saltType: SaltType.ActiveDirectoryUser - ); - }); - } - - return key; - } - - private static EncryptionType ExtractEType(string principalName) - { - if (principalName.StartsWith("RC4", StringComparison.InvariantCultureIgnoreCase)) - { - return EncryptionType.RC4_HMAC_NT; - } - else if (principalName.StartsWith("AES128SHA256", StringComparison.InvariantCultureIgnoreCase)) - { - return EncryptionType.AES128_CTS_HMAC_SHA256_128; - } - else if (principalName.StartsWith("AES128", StringComparison.InvariantCultureIgnoreCase)) - { - return EncryptionType.AES128_CTS_HMAC_SHA1_96; - } - else if (principalName.StartsWith("AES256SHA384", StringComparison.InvariantCultureIgnoreCase)) - { - return EncryptionType.AES256_CTS_HMAC_SHA384_192; - } - else if (principalName.StartsWith("AES256", StringComparison.InvariantCultureIgnoreCase)) - { - return EncryptionType.AES256_CTS_HMAC_SHA1_96; - } - - return EncryptionType.AES256_CTS_HMAC_SHA1_96; + return new KerberosKey( + password: this._password, + principal: new PrincipalName(PrincipalNameType.NT_PRINCIPAL, Realm, new[] { this.PrincipalName }), + etype: etype, + saltType: SaltType.ActiveDirectoryUser); + }); } public void Validate(X509Certificate2Collection certificates) diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs index 36927da21cbabf..2cdc928fdbd6f3 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakePrincipalService.cs @@ -12,11 +12,18 @@ namespace System.Net.Security.Kerberos; class FakePrincipalService : IPrincipalService { - private readonly string realm; + private readonly string _realm; + private readonly Dictionary _principals; public FakePrincipalService(string realm) { - this.realm = realm; + _realm = realm; + _principals = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + + public void Add(string name, IKerberosPrincipal principal) + { + _principals.Add(name, principal); } public Task FindAsync(KrbPrincipalName principalName, string? realm = null) @@ -26,26 +33,12 @@ public FakePrincipalService(string realm) public IKerberosPrincipal? Find(KrbPrincipalName principalName, string? realm = null) { - IKerberosPrincipal? principal = null; - - bool fallback = false; - - if (principalName.FullyQualifiedName.Contains("-fallback", StringComparison.OrdinalIgnoreCase) && - principalName.Type == PrincipalNameType.NT_ENTERPRISE) + if (_principals.TryGetValue(principalName.FullyQualifiedName, out var principal)) { - principal = null; - fallback = true; + return principal; } - if ((principalName.FullyQualifiedName.EndsWith(this.realm, StringComparison.InvariantCultureIgnoreCase) || - principalName.FullyQualifiedName.StartsWith("krbtgt", StringComparison.InvariantCultureIgnoreCase) || - principalName.Type == PrincipalNameType.NT_PRINCIPAL) - && !fallback) - { - principal = new FakeKerberosPrincipal(principalName.FullyQualifiedName, this.realm); - } - - return principal; + return null; } public X509Certificate2 RetrieveKdcCertificate() diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs index 61803aad5ae81d..9c8c4fa3898ac7 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmReferral.cs @@ -8,20 +8,20 @@ namespace System.Net.Security.Kerberos; class FakeRealmReferral : IRealmReferral { - private readonly KrbKdcReqBody body; + private readonly KrbKdcReqBody _body; public FakeRealmReferral(KrbKdcReqBody body) { - this.body = body; + _body = body; } public IKerberosPrincipal Refer() { - var fqn = this.body.SName.FullyQualifiedName; + var fqn = _body.SName.FullyQualifiedName; var predictedRealm = fqn[(fqn.IndexOf('.') + 1)..]; var krbName = KrbPrincipalName.FromString($"krbtgt/{predictedRealm}"); - return new FakeKerberosPrincipal(krbName.FullyQualifiedName, predictedRealm); + return new FakeKerberosPrincipal(PrincipalType.Service, krbName.FullyQualifiedName, predictedRealm, new byte[16]); } } diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs index 267e9e4982072a..b590fb87b4cabd 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmService.cs @@ -8,24 +8,26 @@ namespace System.Net.Security.Kerberos; class FakeRealmService : IRealmService { - private readonly KerberosCompatibilityFlags compatibilityFlags; + private readonly IPrincipalService _principalService; + private readonly KerberosCompatibilityFlags _compatibilityFlags; - public FakeRealmService(string realm, Krb5Config config, KerberosCompatibilityFlags compatibilityFlags = KerberosCompatibilityFlags.None) + public FakeRealmService(string realm, Krb5Config config, IPrincipalService principalService, KerberosCompatibilityFlags compatibilityFlags = KerberosCompatibilityFlags.None) { - this.Name = realm; - this.Configuration = config; - this.compatibilityFlags = compatibilityFlags; + Name = realm; + Configuration = config; + _principalService = principalService; + _compatibilityFlags = compatibilityFlags; } - public IRealmSettings Settings => new FakeRealmSettings(this.compatibilityFlags); + public IRealmSettings Settings => new FakeRealmSettings(_compatibilityFlags); - public IPrincipalService Principals => new FakePrincipalService(this.Name); + public IPrincipalService Principals => _principalService; - public string Name { get; } + public string Name { get; private set; } public DateTimeOffset Now() => DateTimeOffset.UtcNow; public ITrustedRealmService TrustedRealms => new FakeTrustedRealms(this.Name); - public Krb5Config Configuration { get; } + public Krb5Config Configuration { get; private set; } } \ No newline at end of file diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs index 60ab60127d96b4..8e14ea8d456987 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeRealmSettings.cs @@ -7,11 +7,11 @@ namespace System.Net.Security.Kerberos; class FakeRealmSettings : IRealmSettings { - private readonly KerberosCompatibilityFlags compatibilityFlags; + private readonly KerberosCompatibilityFlags _compatibilityFlags; public FakeRealmSettings(KerberosCompatibilityFlags compatibilityFlags) { - this.compatibilityFlags = compatibilityFlags; + _compatibilityFlags = compatibilityFlags; } public TimeSpan MaximumSkew => TimeSpan.FromMinutes(5); @@ -20,5 +20,5 @@ public FakeRealmSettings(KerberosCompatibilityFlags compatibilityFlags) public TimeSpan MaximumRenewalWindow => TimeSpan.FromDays(7); - public KerberosCompatibilityFlags Compatibility => this.compatibilityFlags; + public KerberosCompatibilityFlags Compatibility => _compatibilityFlags; } diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs index de54ebf8f6451b..11fc0208880d22 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/FakeTrustedRealms.cs @@ -8,16 +8,16 @@ namespace System.Net.Security.Kerberos; class FakeTrustedRealms : ITrustedRealmService { - private readonly string currentRealm; + private readonly string _currentRealm; public FakeTrustedRealms(string name) { - this.currentRealm = name; + _currentRealm = name; } public IRealmReferral? ProposeTransit(KrbTgsReq tgsReq, PreAuthenticationContext context) { - if (!tgsReq.Body.SName.FullyQualifiedName.EndsWith(this.currentRealm, StringComparison.InvariantCultureIgnoreCase) && + if (!tgsReq.Body.SName.FullyQualifiedName.EndsWith(_currentRealm, StringComparison.InvariantCultureIgnoreCase) && !tgsReq.Body.SName.FullyQualifiedName.Contains("not.found")) { return new FakeRealmReferral(tgsReq.Body); diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs index 556e0facd8eeb9..b4b7f097dba5f7 100644 --- a/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/KerberosExecutor.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Cryptography; +using System.Text; using System.Threading.Tasks; using Kerberos.NET.Configuration; using Kerberos.NET.Crypto; @@ -17,17 +19,20 @@ namespace System.Net.Security.Kerberos; public class KerberosExecutor : IDisposable { - private ListenerOptions _options; - private string _realm; - private FakeKdcServer _kdcListener; + private readonly ListenerOptions _options; + private readonly string _realm; + private readonly FakePrincipalService _principalService; + private readonly FakeKdcServer _kdcListener; private RemoteInvokeHandle? _invokeHandle; private string? _krb5Path; private string? _keytabPath; - private List _services; + private readonly List _servicePrincipals; public static bool IsSupported { get; } = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); - public static string FakePassword { get; } = "P@ssw0rd!"; + public const string DefaultAdminPassword = "PLACEHOLDERadmin."; + + public const string DefaultUserPassword = "PLACEHOLDERcorrect20"; public KerberosExecutor(ITestOutputHelper testOutputHelper, string realm) { @@ -39,18 +44,27 @@ public KerberosExecutor(ITestOutputHelper testOutputHelper, string realm) testOutputHelper.WriteLine($"[{level}] [{categoryName}] {log}") ); + _principalService = new FakePrincipalService(realm); + + byte[] krbtgtPassword = new byte[16]; + //RandomNumberGenerator.Fill(krbtgtPassword); + + var krbtgt = new FakeKerberosPrincipal(PrincipalType.Service, "krbtgt", realm, krbtgtPassword); + _principalService.Add("krbtgt", krbtgt); + _principalService.Add($"krbtgt/{realm}", krbtgt); + _options = new ListenerOptions { Configuration = krb5Config, DefaultRealm = realm, - RealmLocator = realm => new FakeRealmService(realm, krb5Config), + RealmLocator = realm => new FakeRealmService(realm, krb5Config, _principalService), Log = logger, IsDebug = true, }; _kdcListener = new FakeKdcServer(_options); - _services = new List(); _realm = realm; + _servicePrincipals = new List(); } public void Dispose() @@ -61,9 +75,18 @@ public void Dispose() File.Delete(_keytabPath); } - public void AddService(string name) + public void AddService(string name, string password = DefaultAdminPassword) + { + var principal = new FakeKerberosPrincipal(PrincipalType.Service, name, _realm, Encoding.Unicode.GetBytes(password)); + _principalService.Add(name, principal); + _servicePrincipals.Add(principal); + } + + public void AddUser(string name, string password = DefaultUserPassword) { - _services.Add(name); + var principal = new FakeKerberosPrincipal(PrincipalType.User, name, _realm, Encoding.Unicode.GetBytes(password)); + _principalService.Add(name, principal); + _principalService.Add($"{name}@{_realm}", principal); } public async Task Invoke(Action method) @@ -95,23 +118,14 @@ private async Task PrepareInvoke() var keyTable = new KeyTable(); var etypes = _options.Configuration.Defaults.DefaultTgsEncTypes; - byte[] passwordBytes = FakeKerberosPrincipal.FakePassword; + //byte[] passwordBytes = FakeKerberosPrincipal.FakePassword; - foreach (string service in _services) + foreach (var servicePrincipal in _servicePrincipals) { foreach (var etype in etypes.Where(CryptoService.SupportsEType)) { - var kerbKey = new KerberosKey( - password: passwordBytes, - etype: etype, - principal: new PrincipalName( - PrincipalNameType.NT_PRINCIPAL, - _options.DefaultRealm, - new [] { service }), - saltType: SaltType.ActiveDirectoryUser - ); - - keyTable.Entries.Add(new KeyEntry(kerbKey)); + var kerbKey = servicePrincipal.RetrieveLongTermCredential(etype); + keyTable.Entries.Add(new KeyEntry(kerbKey)); } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs index ca10f8e15e23cd..51f5c3d789e5ff 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs @@ -19,18 +19,19 @@ public NegotiateAuthenticationKerberosTest(ITestOutputHelper testOutputHelper) } [Fact] - public async Task Loopback_Kerberos_Authentication() + public async Task Loopback_Success() { using var kerberosExecutor = new KerberosExecutor(_testOutputHelper, "LINUX.CONTOSO.COM"); kerberosExecutor.AddService("HTTP/linux.contoso.com"); + kerberosExecutor.AddUser("user"); await kerberosExecutor.Invoke(() => { // Do a loopback authentication NegotiateAuthenticationClientOptions clientOptions = new() { - Credential = new NetworkCredential("user", KerberosExecutor.FakePassword, "LINUX.CONTOSO.COM"), + Credential = new NetworkCredential("user", KerberosExecutor.DefaultUserPassword, "LINUX.CONTOSO.COM"), TargetName = $"HTTP/linux.contoso.com" }; NegotiateAuthenticationServerOptions serverOptions = new() { }; @@ -59,5 +60,26 @@ await kerberosExecutor.Invoke(() => Assert.True(serverNegotiateAuthentication.IsAuthenticated); }); } + + [Fact] + public async Task Incorrect_Credentials() + { + using var kerberosExecutor = new KerberosExecutor(_testOutputHelper, "LINUX.CONTOSO.COM"); + kerberosExecutor.AddUser("user"); + await kerberosExecutor.Invoke(() => + { + // Do a loopback authentication + NegotiateAuthenticationClientOptions clientOptions = new() + { + Credential = new NetworkCredential("user", "PLACEHOLDERwrong", "LINUX.CONTOSO.COM"), + TargetName = $"HTTP/linux.contoso.com", + Package = "Kerberos", + }; + NegotiateAuthentication clientNegotiateAuthentication = new(clientOptions); + byte[]? clientBlob = clientNegotiateAuthentication.GetOutgoingBlob((ReadOnlySpan)default, out NegotiateAuthenticationStatusCode statusCode); + Assert.True(statusCode >= NegotiateAuthenticationStatusCode.GenericFailure, $"Client authentication succeeded with {statusCode}"); + Assert.Null(clientBlob); + }); + } } } From 67999aa22ae5d0174ec03fada128ab431329255d Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 12 Jul 2022 09:31:49 +0000 Subject: [PATCH 08/11] Add README --- .../System/Net/Security/Kerberos/README.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/libraries/Common/tests/System/Net/Security/Kerberos/README.md diff --git a/src/libraries/Common/tests/System/Net/Security/Kerberos/README.md b/src/libraries/Common/tests/System/Net/Security/Kerberos/README.md new file mode 100644 index 00000000000000..df8bfc792e15a4 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Security/Kerberos/README.md @@ -0,0 +1,53 @@ +# Kerberos Testing Environment + +## Introduction + +In a typical enterprise setting, there is a domain controller that handles authentication requests and clients connecting to it. The clients here are referring both to the clients in a traditional sense (eg. `HttpClient`) and the services that are running on the server-side (eg. `HttpListener`). + +Replicating that environment for unit testing is non-trivial since it usually requires multiple machines and adjusting system configuration. Alternatively, it could be set up using containers running on a single machine with a preprepared configuration. Unfortunately, using containers restricts the possibility of testing various operating systems, or at least makes it non-trivial. + +To make the setup more approachable, this directory contains an implementation of `KerberosExecutor` class that sets up a virtual environment with Kerberos Domain Controller (KDC) running inside the unit test. The system configuration is then locally redirected for the test itself through environment variables to connect to this KDC instead of a system one. All the tests are run within a `RemoteExecutor` environment. The KDC is powered by the [Kerberos.NET](https://github.com/dotnet/Kerberos.NET) library. + +## Usage + +Since the environment currently works only on Linux and macOS platforms the test class or test method needs to be decorated to only run when `KerberosExecutor` is supported: + +```csharp +[ConditionalClass(typeof(KerberosExecutor), nameof(KerberosExecutor.IsSupported))] +public class MyKerberosTest +``` + +The xUnit logging through `ITestOutputHelper` must be set up for the test class: + +```csharp +private readonly ITestOutputHelper _testOutputHelper; + +public MyKerberosTest(ITestOutputHelper testOutputHelper) +{ + _testOutputHelper = testOutputHelper; +} +``` + +Each test then uses the `KerberosExecutor` class to set up the virtual environment and the test code to run inside the virtual environment: + +```csharp +[Fact] +public async Task Loopback_Success() +{ + using var kerberosExecutor = new KerberosExecutor(_testOutputHelper, "LINUX.CONTOSO.COM"); + + kerberosExecutor.AddService("HTTP/linux.contoso.com"); + kerberosExecutor.AddUser("user"); + + await kerberosExecutor.Invoke(() => + { + // Test code + } +} +``` + +The test itself can add its own users and services. Each service must be specified using full service principal name (SPN). In the example above the default password is used for the user, so the test would use `new NetworkCredential("user", KerberosExecutor.DefaultUserPassword, "LINUX.CONTOSO.COM")` to construct credentials for use in `HttpClient` or similar scenario. + +## Logging + +For failed unit test the verbose output of the KDC server is logged into the test output. If the information is insufficient it is possible to get a trace from the native libraries by setting the `KRB5_TRACE="/dev/stdout"` environment variable. \ No newline at end of file From 98c4f75768261b7893bd5f21b8a6cc45affac910 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 12 Jul 2022 15:09:10 +0000 Subject: [PATCH 09/11] Revert "Add support for specifying Kerberos package on Linux/macOS (in addition to NTLM and Negotiate)" This reverts commit c6619e191e51a6d1400ff1237a971a6277db13ce. --- .../Interop.NetSecurityNative.PackageType.cs | 17 ------ .../Interop.NetSecurityNative.cs | 14 ++--- .../Win32/SafeHandles/GssSafeHandles.cs | 6 +- .../Net/Security/NegotiateStreamPal.Unix.cs | 48 +++++---------- .../Security/Unix/SafeFreeNegoCredentials.cs | 14 ++--- .../src/System.Net.Security.csproj | 2 - .../System.Net.Security.Unit.Tests.csproj | 2 - .../System.Net.Security.Native/pal_gssapi.c | 59 ++++++++----------- .../System.Net.Security.Native/pal_gssapi.h | 13 +--- 9 files changed, 58 insertions(+), 117 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs deleted file mode 100644 index d8d6073c3d04a7..00000000000000 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.PackageType.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -internal static partial class Interop -{ - internal static partial class NetSecurityNative - { - internal enum PackageType : uint - { - Negotiate = 0, - NTLM = 1, - Kerberos = 2, - } - } -} diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs index 84a31968d79378..3ce98eb5d30769 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs @@ -61,7 +61,7 @@ internal static partial Status InitiateCredSpNego( [LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredWithPassword", StringMarshalling = StringMarshalling.Utf8)] internal static partial Status InitiateCredWithPassword( out Status minorStatus, - PackageType packageType, + [MarshalAs(UnmanagedType.Bool)] bool isNtlm, SafeGssNameHandle desiredName, string password, int passwordLen, @@ -77,7 +77,7 @@ private static partial Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - PackageType packageType, + [MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly, SafeGssNameHandle? targetName, uint reqFlags, ref byte inputBytes, @@ -91,7 +91,7 @@ private static partial Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - PackageType packageType, + [MarshalAs(UnmanagedType.Bool)] bool isNtlmOnly, IntPtr cbt, int cbtSize, SafeGssNameHandle? targetName, @@ -106,7 +106,7 @@ internal static Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - PackageType packageType, + bool isNtlmOnly, SafeGssNameHandle? targetName, uint reqFlags, ReadOnlySpan inputBytes, @@ -118,7 +118,7 @@ internal static Status InitSecContext( out minorStatus, initiatorCredHandle, ref contextHandle, - packageType, + isNtlmOnly, targetName, reqFlags, ref MemoryMarshal.GetReference(inputBytes), @@ -132,7 +132,7 @@ internal static Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, ref SafeGssContextHandle contextHandle, - PackageType packageType, + bool isNtlmOnly, IntPtr cbt, int cbtSize, SafeGssNameHandle? targetName, @@ -146,7 +146,7 @@ internal static Status InitSecContext( out minorStatus, initiatorCredHandle, ref contextHandle, - packageType, + isNtlmOnly, cbt, cbtSize, targetName, diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs index acc95913c863c5..253e0548dea9d9 100644 --- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs +++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.cs @@ -91,9 +91,9 @@ public static SafeGssCredHandle CreateAcceptor() /// returns the handle for the given credentials. /// The method returns an invalid handle if the username is null or empty. /// - public static SafeGssCredHandle Create(string username, string password, Interop.NetSecurityNative.PackageType packageType) + public static SafeGssCredHandle Create(string username, string password, bool isNtlmOnly) { - if (packageType == Interop.NetSecurityNative.PackageType.NTLM && !s_IsNtlmInstalled.Value) + if (isNtlmOnly && !s_IsNtlmInstalled.Value) { throw new Interop.NetSecurityNative.GssApiException( Interop.NetSecurityNative.Status.GSS_S_BAD_MECH, @@ -117,7 +117,7 @@ public static SafeGssCredHandle Create(string username, string password, Interop } else { - status = Interop.NetSecurityNative.InitiateCredWithPassword(out minorStatus, packageType, userHandle, password, Encoding.UTF8.GetByteCount(password), out retHandle); + status = Interop.NetSecurityNative.InitiateCredWithPassword(out minorStatus, isNtlmOnly, userHandle, password, Encoding.UTF8.GetByteCount(password), out retHandle); } if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 702167758da69c..73043391901aef 100644 --- a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -134,7 +134,7 @@ private static SecurityStatusPal EstablishSecurityContext( out byte[]? resultBuffer, ref ContextFlagsPal outFlags) { - Interop.NetSecurityNative.PackageType packageType = credential.PackageType; + bool isNtlmOnly = credential.IsNtlmOnly; resultBuffer = null; @@ -142,11 +142,7 @@ private static SecurityStatusPal EstablishSecurityContext( { if (NetEventSource.Log.IsEnabled()) { - string protocol = packageType switch { - Interop.NetSecurityNative.PackageType.NTLM => "NTLM", - Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos", - _ => "SPNEGO" - }; + string protocol = isNtlmOnly ? "NTLM" : "SPNEGO"; NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}"); } @@ -176,7 +172,7 @@ private static SecurityStatusPal EstablishSecurityContext( status = Interop.NetSecurityNative.InitSecContext(out minorStatus, credential.GssCredential, ref contextHandle, - packageType, + isNtlmOnly, cbtAppData, cbtAppDataSize, negoContext.TargetName, @@ -191,7 +187,7 @@ private static SecurityStatusPal EstablishSecurityContext( status = Interop.NetSecurityNative.InitSecContext(out minorStatus, credential.GssCredential, ref contextHandle, - packageType, + isNtlmOnly, negoContext.TargetName, (uint)inputFlags, incomingBlob, @@ -220,11 +216,7 @@ private static SecurityStatusPal EstablishSecurityContext( { if (NetEventSource.Log.IsEnabled()) { - string protocol = packageType switch { - Interop.NetSecurityNative.PackageType.NTLM => "NTLM", - Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos", - _ => isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos" - }; + string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"; NetEventSource.Info(context, $"actual protocol = {protocol}"); } @@ -449,36 +441,24 @@ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, boo { bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) || string.IsNullOrWhiteSpace(credential.Password); - Interop.NetSecurityNative.PackageType packageType; - - if (string.Equals(package, NegotiationInfoClass.Negotiate, StringComparison.OrdinalIgnoreCase)) + bool ntlmOnly = string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase); + if (ntlmOnly && isEmptyCredential && !isServer) { - packageType = Interop.NetSecurityNative.PackageType.Negotiate; + // NTLM authentication is not possible with default credentials which are no-op + throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred); } - else if (string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase)) - { - packageType = Interop.NetSecurityNative.PackageType.NTLM; - if (isEmptyCredential && !isServer) - { - // NTLM authentication is not possible with default credentials which are no-op - throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred); - } - } - else if (string.Equals(package, NegotiationInfoClass.Kerberos, StringComparison.OrdinalIgnoreCase)) - { - packageType = Interop.NetSecurityNative.PackageType.Kerberos; - } - else + + if (!ntlmOnly && !string.Equals(package, NegotiationInfoClass.Negotiate)) { - // Native shim currently supports only NTLM, Negotiate and Kerberos + // Native shim currently supports only NTLM and Negotiate throw new PlatformNotSupportedException(SR.net_securitypackagesupport); } try { return isEmptyCredential ? - new SafeFreeNegoCredentials(packageType, string.Empty, string.Empty, string.Empty) : - new SafeFreeNegoCredentials(packageType, credential.UserName, credential.Password, credential.Domain); + new SafeFreeNegoCredentials(ntlmOnly, string.Empty, string.Empty, string.Empty) : + new SafeFreeNegoCredentials(ntlmOnly, credential.UserName, credential.Password, credential.Domain); } catch (Exception ex) { diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs index ffbd46db32aaf4..58d1942829e524 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs @@ -12,7 +12,7 @@ namespace System.Net.Security internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials { private SafeGssCredHandle _credential; - private readonly Interop.NetSecurityNative.PackageType _packageType; + private readonly bool _isNtlmOnly; private readonly string _userName; private readonly bool _isDefault; @@ -21,10 +21,10 @@ public SafeGssCredHandle GssCredential get { return _credential; } } - // Property represents which protocol is specfied (Negotiate, Ntlm or Kerberos). - public Interop.NetSecurityNative.PackageType PackageType + // Property represents if Ntlm Protocol is specfied or not. + public bool IsNtlmOnly { - get { return _packageType; } + get { return _isNtlmOnly; } } public string UserName @@ -37,7 +37,7 @@ public bool IsDefault get { return _isDefault; } } - public SafeFreeNegoCredentials(Interop.NetSecurityNative.PackageType packageType, string username, string password, string domain) + public SafeFreeNegoCredentials(bool isNtlmOnly, string username, string password, string domain) : base(IntPtr.Zero, true) { Debug.Assert(username != null && password != null, "Username and Password can not be null"); @@ -66,10 +66,10 @@ public SafeFreeNegoCredentials(Interop.NetSecurityNative.PackageType packageType } bool ignore = false; - _packageType = packageType; + _isNtlmOnly = isNtlmOnly; _userName = username; _isDefault = string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password); - _credential = SafeGssCredHandle.Create(username, password, packageType); + _credential = SafeGssCredHandle.Create(username, password, isNtlmOnly); _credential.DangerousAddRef(ref ignore); } diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 55d140a914f553..139ea7ce4ba064 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -252,8 +252,6 @@ Link="Common\Interop\Unix\Interop.Errors.cs" /> - - Date: Tue, 12 Jul 2022 15:11:05 +0000 Subject: [PATCH 10/11] Remove Incorrect_Credentials test --- .../NegotiateAuthenticationKerberosTest.cs | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs index 51f5c3d789e5ff..7cfdf5455b01e7 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs @@ -60,26 +60,5 @@ await kerberosExecutor.Invoke(() => Assert.True(serverNegotiateAuthentication.IsAuthenticated); }); } - - [Fact] - public async Task Incorrect_Credentials() - { - using var kerberosExecutor = new KerberosExecutor(_testOutputHelper, "LINUX.CONTOSO.COM"); - kerberosExecutor.AddUser("user"); - await kerberosExecutor.Invoke(() => - { - // Do a loopback authentication - NegotiateAuthenticationClientOptions clientOptions = new() - { - Credential = new NetworkCredential("user", "PLACEHOLDERwrong", "LINUX.CONTOSO.COM"), - TargetName = $"HTTP/linux.contoso.com", - Package = "Kerberos", - }; - NegotiateAuthentication clientNegotiateAuthentication = new(clientOptions); - byte[]? clientBlob = clientNegotiateAuthentication.GetOutgoingBlob((ReadOnlySpan)default, out NegotiateAuthenticationStatusCode statusCode); - Assert.True(statusCode >= NegotiateAuthenticationStatusCode.GenericFailure, $"Client authentication succeeded with {statusCode}"); - Assert.Null(clientBlob); - }); - } } } From b2e8daf3207f2e82a8b55b064c9407df5708848d Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 12 Jul 2022 20:33:55 +0200 Subject: [PATCH 11/11] Add test for #71463 with invalid tokens --- .../NegotiateAuthenticationKerberosTest.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs index 7cfdf5455b01e7..3bb9a1ad5cc2f8 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/NegotiateAuthenticationKerberosTest.cs @@ -60,5 +60,25 @@ await kerberosExecutor.Invoke(() => Assert.True(serverNegotiateAuthentication.IsAuthenticated); }); } + + [Fact] + public async void Invalid_Token() + { + using var kerberosExecutor = new KerberosExecutor(_testOutputHelper, "LINUX.CONTOSO.COM"); + // Force a non-empty keytab to make macOS happy + kerberosExecutor.AddService("HTTP/linux.contoso.com"); + await kerberosExecutor.Invoke(() => + { + NegotiateAuthentication ntAuth = new NegotiateAuthentication(new NegotiateAuthenticationServerOptions { }); + // Ask for NegHints + byte[] blob = ntAuth.GetOutgoingBlob((ReadOnlySpan)default, out NegotiateAuthenticationStatusCode statusCode); + Assert.Equal(NegotiateAuthenticationStatusCode.ContinueNeeded, statusCode); + Assert.NotNull(blob); + // Send garbage token + blob = ntAuth.GetOutgoingBlob(new byte[3], out statusCode); + Assert.True(statusCode >= NegotiateAuthenticationStatusCode.GenericFailure); + Assert.Null(blob); + }); + } } }