diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs
index 3c3cdb20c59eb..57b8f025b88ba 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs
@@ -254,7 +254,12 @@ private static void LoadMachineStores()
{
using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb"))
{
- Interop.Crypto.CheckValidOpenSslHandle(fileBio);
+ // The handle may be invalid, for example when we don't have read permission for the file.
+ if (fileBio.IsInvalid)
+ {
+ Interop.Crypto.ErrClearError();
+ continue;
+ }
ICertificatePal pal;
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
index 09f919a4f4386..f1d383dff6d5e 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
@@ -90,6 +90,12 @@
+
+
+ {69e46a6f-9966-45a5-8945-2559fe337827}
+ RemoteExecutorConsoleApp
+
+
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs
index 25b55d437b9d9..ff8c1614be5a4 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs
@@ -80,6 +80,78 @@ internal static class TestData
XgSpRm3m9Xp5QL0fzehF1a7iXT71dcfmZmNgzNWahIeNJDD37zTQYx2xQmdKDku/
Og7vtpU6pzjkJZIIpohmgg==
-----END CERTIFICATE-----
+");
+
+ // 'cert.pem' generated using 'openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365'
+ public static readonly byte[] SelfSigned1PemBytes = ByteUtils.AsciiBytes(
+ @"-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIJAJpCQ7mtFWHeMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV
+BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
+Q29tcGFueSBMdGQwHhcNMTgwNTMwMTgyNjM1WhcNMTkwNTMwMTgyNjM1WjBCMQsw
+CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh
+dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+pfYZTHjzei9U3QxiIIjESsf9z3Bfl8FAQLIU+OeICN3upnDvTgeWM/Jw7LwiuHhu
+XvSawPwQ8ONvUeSG/wfyjYyTB7VBpVnNi6oTR6E1WSuiu0iT3qlDHVwArTI5DvIM
+FzP3/AT1Ub5SvwVbWiR2za6wuUIsryyLz5+zCwGr+J/Xbmta/H9IT9NLwmDJCZQe
+4Q4hCWhf7FKdXWt59y9PofVnE7R8CKNfUKr6GA+gy+SEtM/cHgqox5PErnV9b14U
+uVROnRUyo1bFwTOdoW3zf5S4VZ4pFPJHNYACHEMiE0eNgfJf+QeyPUPN50neEAbf
+kQYkeEET8dW6JlDFrAI4wwIDAQABo1MwUTAdBgNVHQ4EFgQUK+C/eGYPlV+KaTvj
+tF6lJaKmo3EwHwYDVR0jBBgwFoAUK+C/eGYPlV+KaTvjtF6lJaKmo3EwDwYDVR0T
+AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAZUjvDMhGc45TLRHKO5rsyifN
+g7qb3dO5vtD/JWeo+wyMYcBHIANIIxYrkT0dRBQWQerVDBvsAESahM3f0SdszGac
+6y1qxQWxfjxRiCwrEQ7JVZkmspYLbOxaS1T2IZUo3D7VJReyna6r11EKy7i49Toa
+KmrhTLBsHV+MUgPRtupiOOu0fXqfxpXE7XEvi0hyv8PKli+Oww2Zyt1jTTvv2RTA
+eJRqTUNUbWEDesXAOh5CY6Xjfg7Gt6IYQHt0JMw29pXB3TV2uyXuvFNsc725cPbW
+JCuC9TGQRUAUj+LZ43tTrfaZ7g5L80/eRrvlx5MIJSsX8cev8pZYx224WRtk/w==
+-----END CERTIFICATE-----
+");
+
+ // 'cert.pem' generated using 'openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365'
+ public static readonly byte[] SelfSigned2PemBytes = ByteUtils.AsciiBytes(
+ @"-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIJAM6YQ4PrC9jaMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV
+BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
+Q29tcGFueSBMdGQwHhcNMTgwNTMwMTgyNjQ4WhcNMTkwNTMwMTgyNjQ4WjBCMQsw
+CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh
+dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+7vkM6zrhXJFtrV63lUb4fsjZG2JYvSRGYv4Y/rwe7VLVdTYvMjosyvKCHJ4Frmtb
+YU4jJeB+859mQAd3IOBEhgUJuJ6gC8cOJAwUFJNUabeuafXG2zw/U+396csRKr11
+iBUpvooFJR7KLWrqPKXhK5yESV1k7OzSSZs4owmyIvSaGQO2T63S39OYJhq8ZUlO
++MznaOQGp2J+JWncZo9XCpiotZwdNtw5k+F1g3NAz4/+Vkah/SfQhcNCfJyfVDCX
+IwBS+gz9i1BIw6s+SLYtkp167yyizmVIWoXtkgCPaeG0FzBPAhL9GDLTItJ/V/O5
+F9SjbvS+4rUIuPSn7NdodwIDAQABo1MwUTAdBgNVHQ4EFgQUq4v4TrvYrsbKDRGF
+bMnj3++P9B4wHwYDVR0jBBgwFoAUq4v4TrvYrsbKDRGFbMnj3++P9B4wDwYDVR0T
+AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAS4ZKEGVfIrHMmYAZe9p3jm15
+85OIPLlM4q6QjRccLJ4t2+xYLRU9PNa2Qmz8l+SFOy9XN9yJp79lSi1gN4lJ8ZdI
+kwu+zPEzwsJnb6f3zRu2RQUeAkaShDCEdxpyKHEky1KVG2nOa3cKp+pqzN4DQ3Rp
+cJCjcP5ncNJ0bbCZTS7w0oVvX5JhBWIigw3CN5rL2rf83CTPPBzUype0bt97sBSs
+dxIPtH9l/q9OgdaCrPE8KBqcwXsfNlFwYGjkqmN/v7WaysBRdblHcoWmry3YsaK2
+/tZo6lmYOHpdqL0OdDwlldToY7QdL1coICfHas5Ony49OHTCUZz6G/AS+3a3gQ==
+-----END CERTIFICATE-----
+");
+
+ // 'cert.pem' generated using 'openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365'
+ public static readonly byte[] SelfSigned3PemBytes = ByteUtils.AsciiBytes(
+ @"-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIJANzv9IQvr0bwMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV
+BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
+Q29tcGFueSBMdGQwHhcNMTgwNjA0MTMzMjIxWhcNMTkwNjA0MTMzMjIxWjBCMQsw
+CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh
+dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+wy+py+hFxSmCGTZmHrQm1Yobzxf34l+J8VD33ObGV1qIFFulxz8pnUU4gKf6FQNU
+wAbezJ78Eqsjt4c7mwnGTdavWSZyDJ136bQzn52wsTOGRfUBe1vt+SMy7h8Nhhf3
+ejRHQVsZKNfiGOekmjBKFLliavo6I8j80UsmpvAJ+TWnYpVQBf/EzBQ21ddIF6jD
+nl2ZhcvWHvS63utWwXW68xkDXsLvjiat22YScRKnkkNAIvbBY4rvV1KwahUPaMTS
+zWywr6caHxlKp7McZ4MJVIqUAeZUn4KYgSksi2IsfPA7qi8WpSaKGsOZFBD79DJC
+wqzdLLBzEtg6okzgC5nFtwIDAQABo1MwUTAdBgNVHQ4EFgQUgKAUBaaA1XD8KqGg
+1XEr74W4lrkwHwYDVR0jBBgwFoAUgKAUBaaA1XD8KqGg1XEr74W4lrkwDwYDVR0T
+AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEArNBpG8oCDKX9ERbMgvgm3qWk
+FKmx+h58aiZVoMwfBsf2njZ6BzRoEOvluMDPe+pt8hhST5yaOsMUYIqrn+s692I9
+17JRfrFhCp+4GT8oe/ZnSNTPm2zOzm0VXFkfDF53YGzdGTWXLH+pJpw4drCNoBoA
+yloyF/JJGJ2ZMbnwuDtsPbpjup8qHLiQYjxj4hUWyXU+nbytGK/i8z8HHc7acOpd
+9+MXEcKwUkthXzG0M/0bzz4GwWZ6PPmbI5EEqFGBzMef58/mbHDigl9/o3kUlJtB
+tcCZhP5KEu6XKKc1GcTqbyA0vi92YyyZViUa36hhVrNqPxtpclir+lcnNgnlqg==
+-----END CERTIFICATE-----
");
public const string PfxDataPassword = "12345";
diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs
index 2bc94e2fec556..151067587bf6a 100644
--- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs
+++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs
@@ -2,11 +2,14 @@
// 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.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
using Xunit;
namespace System.Security.Cryptography.X509Certificates.Tests
{
- public class X509StoreTests
+ public class X509StoreTests : RemoteExecutorTestBase
{
[Fact]
public static void OpenMyStore()
@@ -496,5 +499,48 @@ public static void UnixCannotModifyDisallowedStore(bool useEnum, OpenFlags openF
Assert.Equal(0, store.Certificates.Count);
}
}
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Linux)] // Windows/OSX doesn't use SSL_CERT_{DIR,FILE}.
+ private void X509Store_MachineStoreLoadSkipsInvalidFiles()
+ {
+ // We create a folder for our machine store and use it by setting SSL_CERT_{DIR,FILE}.
+ // In the store we'll add some invalid files, but we start and finish with a valid file.
+ // This is to account for the order in which the store is populated.
+ string sslCertDir = GetTestFilePath();
+ Directory.CreateDirectory(sslCertDir);
+
+ // Valid file.
+ File.WriteAllBytes(Path.Combine(sslCertDir, "0.pem"), TestData.SelfSigned1PemBytes);
+
+ // File with invalid content.
+ File.WriteAllText(Path.Combine(sslCertDir, "1.pem"), "This is not a valid cert");
+
+ // File which is not readable by the current user.
+ string unreadableFileName = Path.Combine(sslCertDir, "2.pem");
+ File.WriteAllBytes(unreadableFileName, TestData.SelfSigned2PemBytes);
+ Assert.Equal(0, chmod(unreadableFileName, 0));
+
+ // Valid file.
+ File.WriteAllBytes(Path.Combine(sslCertDir, "3.pem"), TestData.SelfSigned3PemBytes);
+
+ var psi = new ProcessStartInfo();
+ psi.Environment.Add("SSL_CERT_DIR", sslCertDir);
+ psi.Environment.Add("SSL_CERT_FILE", "/nonexisting");
+ RemoteInvoke(() =>
+ {
+ using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
+ {
+ store.Open(OpenFlags.OpenExistingOnly);
+
+ // Check nr of certificates in store.
+ Assert.Equal(2, store.Certificates.Count);
+ }
+ return SuccessExitCode;
+ }, new RemoteInvokeOptions { StartInfo = psi }).Dispose();
+ }
+
+ [DllImport("libc")]
+ private static extern int chmod(string path, int mode);
}
}