Imported Upstream version 6.0.0.172

Former-commit-id: f3cc9b82f3e5bd8f0fd3ebc098f789556b44e9cd
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-04-12 14:10:50 +00:00
parent 8016999e4d
commit 64ac736ec5
32155 changed files with 3981439 additions and 75368 deletions

View File

@@ -3,19 +3,17 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
namespace Internal.Cryptography.Pal
{
internal static class CertificateAssetDownloader
{
private static readonly Interop.Http.ReadWriteCallback s_writeCallback = CurlWriteCallback;
internal static X509Certificate2 DownloadCertificate(string uri, ref TimeSpan remainingDownloadTime)
{
byte[] data = DownloadAsset(uri, ref remainingDownloadTime);
@@ -75,99 +73,37 @@ namespace Internal.Cryptography.Pal
private static byte[] DownloadAsset(string uri, ref TimeSpan remainingDownloadTime)
{
if (remainingDownloadTime <= TimeSpan.Zero)
if (remainingDownloadTime > TimeSpan.Zero)
{
return null;
}
List<byte[]> dataPieces = new List<byte[]>();
using (Interop.Http.SafeCurlHandle curlHandle = Interop.Http.EasyCreate())
{
GCHandle gcHandle = GCHandle.Alloc(dataPieces);
Interop.Http.SafeCallbackHandle callbackHandle = new Interop.Http.SafeCallbackHandle();
Stopwatch stopwatch = Stopwatch.StartNew();
object httpClient = null;
try
{
Interop.Http.EasySetOptionString(curlHandle, Interop.Http.CURLoption.CURLOPT_URL, uri);
Interop.Http.EasySetOptionLong(curlHandle, Interop.Http.CURLoption.CURLOPT_FOLLOWLOCATION, 1L);
IntPtr dataHandlePtr = GCHandle.ToIntPtr(gcHandle);
Interop.Http.RegisterReadWriteCallback(
curlHandle,
Interop.Http.ReadWriteFunction.Write,
s_writeCallback,
dataHandlePtr,
ref callbackHandle);
Stopwatch stopwatch = Stopwatch.StartNew();
Interop.Http.CURLcode res = Interop.Http.EasyPerform(curlHandle);
stopwatch.Stop();
// TimeSpan.Zero isn't a worrisome value on the subtraction, it only
// means "no limit" on the original input.
remainingDownloadTime -= stopwatch.Elapsed;
if (res != Interop.Http.CURLcode.CURLE_OK)
// Use reflection to access System.Net.Http:
// Since System.Net.Http.dll explicitly depends on System.Security.Cryptography.X509Certificates.dll,
// the latter can't in turn have an explicit dependency on the former.
Type httpClientType = Type.GetType("System.Net.Http.HttpClient, System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
if (httpClientType != null)
{
return null;
MethodInfo getByteArrayAsync = httpClientType.GetMethod("GetByteArrayAsync", new Type[] { typeof(string) });
if (getByteArrayAsync != null)
{
httpClient = Activator.CreateInstance(httpClientType);
return ((Task<byte[]>)getByteArrayAsync.Invoke(httpClient, new object[] { uri })).GetAwaiter().GetResult();
}
}
}
catch { }
finally
{
gcHandle.Free();
callbackHandle.Dispose();
(httpClient as IDisposable)?.Dispose();
// TimeSpan.Zero isn't a worrisome value on the subtraction, it only means "no limit" on the original input.
remainingDownloadTime -= stopwatch.Elapsed;
}
}
if (dataPieces.Count == 0)
{
return null;
}
if (dataPieces.Count == 1)
{
return dataPieces[0];
}
int dataLen = 0;
for (int i = 0; i < dataPieces.Count; i++)
{
dataLen += dataPieces[i].Length;
}
byte[] data = new byte[dataLen];
int offset = 0;
for (int i = 0; i < dataPieces.Count; i++)
{
byte[] piece = dataPieces[i];
Buffer.BlockCopy(piece, 0, data, offset, piece.Length);
offset += piece.Length;
}
return data;
}
private static ulong CurlWriteCallback(IntPtr buffer, ulong size, ulong nitems, IntPtr context)
{
ulong totalSize = size * nitems;
if (totalSize == 0)
{
return 0;
}
GCHandle gcHandle = GCHandle.FromIntPtr(context);
List<byte[]> dataPieces = (List<byte[]>)gcHandle.Target;
byte[] piece = new byte[totalSize];
Marshal.Copy(buffer, piece, 0, (int)totalSize);
dataPieces.Add(piece);
return totalSize;
return null;
}
}
}

View File

@@ -244,7 +244,7 @@ namespace Internal.Cryptography.Pal
if (rootStoreFile != null && rootStoreFile.Exists)
{
trustedCertFiles = Append(trustedCertFiles, rootStoreFile);
trustedCertFiles = Prepend(trustedCertFiles, rootStoreFile);
}
HashSet<X509Certificate2> uniqueRootCerts = new HashSet<X509Certificate2>();
@@ -301,12 +301,14 @@ namespace Internal.Cryptography.Pal
Volatile.Write(ref s_machineRootStore, rootStorePal);
}
private static IEnumerable<T> Append<T>(IEnumerable<T> current, T addition)
private static IEnumerable<T> Prepend<T>(IEnumerable<T> current, T addition)
{
foreach (T element in current)
yield return element;
yield return addition;
foreach (T element in current)
{
yield return element;
}
}
}
}

View File

@@ -183,12 +183,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Permissions.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Permissions.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Http.Native\Interop.Initialization.cs">
<Link>Common\Interop\Unix\System.Net.Http.Native\Interop.Initialization.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Http.Native\Interop.VersionInfo.cs">
<Link>Common\Interop\Unix\System.Net.Http.Native\Interop.VersionInfo.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs</Link>
</Compile>
@@ -252,12 +246,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Stat.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Stat.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Http.Native\Interop.CURLcode.cs">
<Link>Common\Interop\Unix\System.Net.Http.Native\Interop.CURLcode.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Http.Native\Interop.Easy.cs">
<Link>Common\Interop\Unix\System.Net.Http.Native\Interop.Easy.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Crypto.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Crypto.cs</Link>
</Compile>

View File

@@ -8,6 +8,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Test.Cryptography;
using Xunit;
namespace System.Security.Cryptography.X509Certificates.Tests
@@ -690,5 +691,25 @@ namespace System.Security.Cryptography.X509Certificates.Tests
Assert.Equal(expectedFlags, allFlags);
}
}
[Fact]
public static void ChainWithEmptySubject()
{
using (var cert = new X509Certificate2(TestData.EmptySubjectCertificate))
using (var issuer = new X509Certificate2(TestData.EmptySubjectIssuerCertificate))
using (ChainHolder chainHolder = new ChainHolder())
{
X509Chain chain = chainHolder.Chain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.ExtraStore.Add(issuer);
Assert.True(chain.Build(cert), "chain.Build(cert)");
Assert.Equal(2, chain.ChainElements.Count);
Assert.Equal(string.Empty, cert.Subject);
Assert.Equal(cert.RawData, chain.ChainElements[0].Certificate.RawData);
Assert.Equal(issuer.RawData, chain.ChainElements[1].Certificate.RawData);
}
}
}
}

View File

@@ -0,0 +1,181 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Xunit;
namespace System.Security.Cryptography.X509Certificates.Tests
{
public static class HostnameMatchTests
{
[Theory]
[InlineData(false, false)]
[InlineData(false, true)]
[InlineData(true, false)]
[InlineData(true, true)]
public static void MatchCN_NoWildcards(bool wantsWildcard, bool mixedCase)
{
string targetName = "LocalHost.loCAldoMaIn";
string subjectCN = wantsWildcard ? "*.LOcaLdomain" : targetName;
RunTest(targetName, subjectCN, null, !mixedCase, !wantsWildcard);
}
[Theory]
[InlineData("Capitalized.SomeDomain.TLD", false, true)]
[InlineData("Capitalized.SomeDomain.TLD", true, true)]
[InlineData("Too.Many.SomeDomain.TLD", false, false)]
[InlineData("Too.Many.SomeDomain.TLD", true, false)]
[InlineData("Now.Lower.SomeDomain.TLD", false, true)]
[InlineData("Now.Lower.SomeDomain.TLD", true, true)]
[InlineData("Score.1812-Overture.somedomain.TLD", false, true)]
[InlineData("Score.1812-Overture.somedomain.TLD", true, true)]
[InlineData("1-800.Lower.somedomain.TLD", false, true)]
[InlineData("1-800.Lower.somedomain.TLD", true, true)]
public static void MatchSubjectAltName(string targetName, bool mixedCase, bool expectedResult)
{
string[] sanEntries =
{
"Capitalized.SomeDomain.TLD",
"*.SomeDomain.TLD",
"*.lower.someDomain.Tld",
"*.1812-Overture.SomeDomain.Tld",
};
RunTest(targetName, "SAN Certificate", sanEntries, !mixedCase, expectedResult);
}
[Fact]
public static void SubjectAltName_NoFallback()
{
string[] sanEntries =
{
"reference.example.org",
"other.example.org",
"reference.example",
};
RunTest("www.example.org", "www.example.org", sanEntries, false, false);
}
private static void RunTest(
string targetName,
string subjectCN,
IList<string> sanDnsNames,
bool flattenCase,
bool expectedResult)
{
using (RSA rsa = RSA.Create(TestData.RsaBigExponentParams))
{
CertificateRequest request = new CertificateRequest(
$"CN={FixCase(subjectCN, flattenCase)}, O=.NET Framework (CoreFX)",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.DigitalSignature,
false));
if (sanDnsNames != null)
{
var builder = new SubjectAlternativeNameBuilder();
foreach (string sanDnsName in sanDnsNames)
{
builder.AddDnsName(sanDnsName);
}
X509Extension extension = builder.Build();
// The SAN builder will have done DNS case normalization via IdnMapping.
// We need to undo that here.
if (!flattenCase)
{
UTF8Encoding encoding = new UTF8Encoding();
byte[] extensionBytes = extension.RawData;
Span<byte> extensionSpan = extensionBytes;
foreach (string sanDnsName in sanDnsNames)
{
// If the string is longer than 127 then the quick DER encoding check
// is not correct.
Assert.InRange(sanDnsName.Length, 1, 127);
byte[] lowerBytes = encoding.GetBytes(sanDnsName.ToLowerInvariant());
byte[] mixedBytes = encoding.GetBytes(sanDnsName);
// Only 7-bit ASCII should be here, no byte expansion.
// (non-7-bit ASCII values require IdnMapping normalization)
Assert.Equal(sanDnsName.Length, lowerBytes.Length);
Assert.Equal(sanDnsName.Length, mixedBytes.Length);
int idx = extensionSpan.IndexOf(lowerBytes);
while (idx >= 0)
{
if (idx < 2 ||
extensionBytes[idx - 2] != 0x82 ||
extensionBytes[idx - 1] != sanDnsName.Length)
{
int relativeIdx = extensionSpan.Slice(idx + 1).IndexOf(lowerBytes);
idx = idx + 1 + relativeIdx;
continue;
}
mixedBytes.AsSpan().CopyTo(extensionSpan.Slice(idx));
break;
}
}
extension.RawData = extensionBytes;
}
request.CertificateExtensions.Add(extension);
}
DateTimeOffset start = DateTimeOffset.UtcNow.AddYears(-1);
DateTimeOffset end = start.AddYears(1);
using (X509Certificate2 cert = request.CreateSelfSigned(start, end))
{
bool isMatch = CheckHostname(cert, targetName);
string lowerTarget = targetName.ToLowerInvariant();
bool isLowerMatch = CheckHostname(cert, lowerTarget);
if (expectedResult)
{
Assert.True(isMatch, $"{targetName} matches");
Assert.True(isLowerMatch, $"{lowerTarget} (lowercase) matches");
}
else
{
Assert.False(isMatch, $"{targetName} matches");
Assert.False(isLowerMatch, $"{lowerTarget} (lowercase) matches");
}
}
}
}
private static string FixCase(string input, bool flatten)
{
return flatten ? input.ToLowerInvariant() : input;
}
private static bool CheckHostname(X509Certificate2 cert, string targetName)
{
int value = CheckX509Hostname(cert.Handle, targetName, targetName.Length);
GC.KeepAlive(cert);
Assert.InRange(value, 0, 1);
return value != 0;
}
[DllImport(Interop.Libraries.CryptoNative, EntryPoint = "CryptoNative_CheckX509Hostname")]
private static extern int CheckX509Hostname(IntPtr x509, string hostname, int cchHostname);
}
}

View File

@@ -98,6 +98,7 @@
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Names.Unix.cs">
<Link>Common\System\IO\PersistedFiles.Names.Unix.cs</Link>
</Compile>
<Compile Include="HostnameMatchTests.Unix.cs" />
<Compile Include="TestEnvironmentConfiguration.Unix.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -1 +1 @@
f27924e0354b11871f2c524d60242f130819ac36
7aaea151234267e947c57023721bda9806555112

View File

@@ -2,10 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#if netcoreapp || uap
#define HAVE_STORE_ISOPEN
#endif
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;