Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add support for X509 revocation checks on Unix #3047

Merged
merged 4 commits into from
Sep 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;
Expand Down Expand Up @@ -33,6 +34,9 @@ internal static partial class Crypto
[DllImport(Libraries.CryptoNative)]
internal static extern IntPtr GetX509NotAfter(SafeX509Handle x509);

[DllImport(Libraries.CryptoNative)]
internal static extern IntPtr GetX509CrlNextUpdate(SafeX509CrlHandle crl);

[DllImport(Libraries.CryptoNative)]
internal static extern int GetX509Version(SafeX509Handle x509);

Expand Down Expand Up @@ -132,12 +136,10 @@ internal static byte[] GetX509PublicKeyParameterBytes(SafeX509Handle x509)

internal static void SetX509ChainVerifyTime(SafeX509StoreCtxHandle ctx, DateTime verifyTime)
{
// Let Unspecified mean Local, so only convert if the source was UTC.
if (verifyTime.Kind == DateTimeKind.Utc)
{
verifyTime = verifyTime.ToLocalTime();
}

// OpenSSL is going to convert our input time to universal, so we should be in Local or
// Unspecified (local-assumed).
Debug.Assert(verifyTime.Kind != DateTimeKind.Utc, "UTC verifyTime should have been normalized to Local");

int succeeded = SetX509ChainVerifyTime(
ctx,
verifyTime.Year,
Expand Down
50 changes: 50 additions & 0 deletions src/Common/src/Interop/Unix/libcrypto/Interop.X509.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ internal static partial class libcrypto
[DllImport(Libraries.LibCrypto)]
internal static extern int X509_check_issued(SafeX509Handle issuer, SafeX509Handle subject);

[DllImport(Libraries.LibCrypto)]
internal static extern NativeULong X509_issuer_name_hash(SafeX509Handle x);

[DllImport(Libraries.LibCrypto)]
internal static extern int X509_get_ext_count(SafeX509Handle x);

Expand Down Expand Up @@ -86,6 +89,14 @@ internal static partial class libcrypto
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool X509_STORE_add_cert(SafeX509StoreHandle ctx, SafeX509Handle x);

[DllImport(Libraries.LibCrypto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool X509_STORE_add_crl(SafeX509StoreHandle ctx, SafeX509CrlHandle x);

[DllImport(Libraries.LibCrypto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool X509_STORE_set_flags(SafeX509StoreHandle ctx, X509VerifyFlags flags);

[DllImport(Libraries.LibCrypto)]
internal static extern SafeX509StoreCtxHandle X509_STORE_CTX_new();

Expand All @@ -110,6 +121,45 @@ internal static partial class libcrypto

[DllImport(Libraries.LibCrypto)]
internal static extern string X509_verify_cert_error_string(X509VerifyStatusCode n);

[DllImport(Libraries.LibCrypto)]
internal static extern void X509_CRL_free(IntPtr a);

[DllImport(Libraries.LibCrypto)]
internal static unsafe extern SafeX509CrlHandle d2i_X509_CRL(IntPtr zero, byte** ppin, int len);

[DllImport(Libraries.LibCrypto)]
internal static extern int PEM_write_bio_X509_CRL(SafeBioHandle bio, SafeX509CrlHandle crl);

[DllImport(Libraries.LibCrypto)]
private static extern SafeX509CrlHandle PEM_read_bio_X509_CRL(SafeBioHandle bio, IntPtr zero, IntPtr zero1, IntPtr zero2);

internal static SafeX509CrlHandle PEM_read_bio_X509_CRL(SafeBioHandle bio)
{
return PEM_read_bio_X509_CRL(bio, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}

// This is "unsigned long" in native, which means ulong on x64, uint on x86
// But we only support x64 for now.
[Flags]
internal enum X509VerifyFlags : ulong
{
None = 0,
X509_V_FLAG_CB_ISSUER_CHECK = 0x0001,
X509_V_FLAG_USE_CHECK_TIME = 0x0002,
X509_V_FLAG_CRL_CHECK = 0x0004,
X509_V_FLAG_CRL_CHECK_ALL = 0x0008,
X509_V_FLAG_IGNORE_CRITICAL = 0x0010,
X509_V_FLAG_X509_STRICT = 0x0020,
X509_V_FLAG_ALLOW_PROXY_CERTS = 0x0040,
X509_V_FLAG_POLICY_CHECK = 0x0080,
X509_V_FLAG_EXPLICIT_POLICY = 0x0100,
X509_V_FLAG_INHIBIT_ANY = 0x0200,
X509_V_FLAG_INHIBIT_MAP = 0x0400,
X509_V_FLAG_NOTIFY_POLICY = 0x0800,

X509_V_FLAG_CHECK_SS_SIGNATURE = 0x4000,
}

internal enum X509VerifyStatusCode
{
Expand Down
21 changes: 21 additions & 0 deletions src/Common/src/Microsoft/Win32/SafeHandles/SafeX509Handles.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ public override bool IsInvalid
get { return handle == IntPtr.Zero; }
}
}

internal sealed class SafeX509CrlHandle : SafeHandle
{
private SafeX509CrlHandle() :
base(IntPtr.Zero, ownsHandle: true)
{
}

protected override bool ReleaseHandle()
{
Interop.libcrypto.X509_CRL_free(handle);
SetHandle(IntPtr.Zero);
return true;
}

public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
}

[SecurityCritical]
internal sealed class SafeX509StoreHandle : SafeHandle
{
Expand Down
23 changes: 23 additions & 0 deletions src/Native/System.Security.Cryptography.Native/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,29 @@ GetX509NotAfter(
return NULL;
}

/*
Function:
GetX509CrlNextUpdate

Used by System.Security.Cryptography.X509Certificates' CrlCache to identify the
end of the validity period of the certificate revocation list in question.

Return values:
NULL if the validity cannot be determined, a pointer to the ASN1_TIME structure for the NextUpdate value
otherwise.
*/
ASN1_TIME*
GetX509CrlNextUpdate(
X509_CRL* crl)
{
if (crl)
{
return X509_CRL_get_nextUpdate(crl);
}

return NULL;
}

/*
Function:
GetX509Version
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22911.2
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.X509Certificates", "src\System.Security.Cryptography.X509Certificates.csproj", "{6F8576C2-6CD0-4DF3-8394-00B002D82E40}"
EndProject
Expand Down Expand Up @@ -29,18 +29,18 @@ Global
{6F8576C2-6CD0-4DF3-8394-00B002D82E40}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU
{6F8576C2-6CD0-4DF3-8394-00B002D82E40}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU
{6F8576C2-6CD0-4DF3-8394-00B002D82E40}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Debug|Any CPU.Build.0 = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Release|Any CPU.ActiveCfg = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Release|Any CPU.Build.0 = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Debug|Any CPU.Build.0 = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Release|Any CPU.ActiveCfg = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Release|Any CPU.Build.0 = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Debug|Any CPU.Build.0 = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Release|Any CPU.ActiveCfg = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Release|Any CPU.Build.0 = Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Debug|Any CPU.ActiveCfg = Linux_Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Debug|Any CPU.Build.0 = Linux_Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Release|Any CPU.ActiveCfg = Linux_Release|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Linux_Release|Any CPU.Build.0 = Linux_Release|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Debug|Any CPU.ActiveCfg = OSX_Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Debug|Any CPU.Build.0 = OSX_Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Release|Any CPU.ActiveCfg = OSX_Release|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.OSX_Release|Any CPU.Build.0 = OSX_Release|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU
{A28B0064-EFB2-4B77-B97C-DECF5DAB074E}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal static class Oids
public const string SubjectKeyIdentifier = "2.5.29.14";
public const string KeyUsage = "2.5.29.15";
public const string BasicConstraints2 = "2.5.29.19";
public const string CrlDistributionPoints = "2.5.29.31";
public const string CertPolicies = "2.5.29.32";
public const string AnyCertPolicy = "2.5.29.32.0";
public const string CertPolicyMappings = "2.5.29.33";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Win32.SafeHandles;

namespace Internal.Cryptography.Pal
{
internal static class CertificateAssetDownloader
{
private static Interop.libcurl.curl_readwrite_callback s_writeCallback = CurlWriteCallback;
private static readonly Interop.libcurl.curl_readwrite_callback s_writeCallback = CurlWriteCallback;

internal static X509Certificate2 DownloadCertificate(string uri, ref TimeSpan remainingDownloadTime)
{
Expand All @@ -30,7 +31,47 @@ internal static X509Certificate2 DownloadCertificate(string uri, ref TimeSpan re
}
}

internal static unsafe byte[] DownloadAsset(string uri, ref TimeSpan remainingDownloadTime)
internal static SafeX509CrlHandle DownloadCrl(string uri, ref TimeSpan remainingDownloadTime)
{
byte[] data = DownloadAsset(uri, ref remainingDownloadTime);

if (data == null)
{
return null;
}

SafeX509CrlHandle handle;

unsafe
{
// DER-encoded CRL seems to be the most common off of some random spot-checking, so try DER first.
handle = Interop.libcrypto.OpenSslD2I(
(ptr, b, i) => Interop.libcrypto.d2i_X509_CRL(ptr, b, i),
data,
checkHandle: false);
}

if (!handle.IsInvalid)
{
return handle;
}

using (SafeBioHandle bio = Interop.libcrypto.BIO_new(Interop.libcrypto.BIO_s_mem()))
{
Interop.libcrypto.BIO_write(bio, data, data.Length);

handle = Interop.libcrypto.PEM_read_bio_X509_CRL(bio);

if (!handle.IsInvalid)
{
return handle;
}
}

return null;
}

private static byte[] DownloadAsset(string uri, ref TimeSpan remainingDownloadTime)
{
if (remainingDownloadTime <= TimeSpan.Zero)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,21 @@ public static IChainPal BuildChain(
DateTime verificationTime,
TimeSpan timeout)
{
CheckRevocationMode(revocationMode);

// An input value of 0 on the timeout is "take all the time you need".
if (timeout == TimeSpan.Zero)
{
timeout = TimeSpan.MaxValue;
}

// Let Unspecified mean Local, so only convert if the source was UTC.
//
// Converge on Local instead of UTC because OpenSSL is going to assume we gave it
// local time.
if (verificationTime.Kind == DateTimeKind.Utc)
{
verificationTime = verificationTime.ToLocalTime();
}

TimeSpan remainingDownloadTime = timeout;
X509Certificate2 leaf = new X509Certificate2(cert.Handle);
List<X509Certificate2> downloaded = new List<X509Certificate2>();
Expand All @@ -51,7 +58,10 @@ public static IChainPal BuildChain(
downloaded,
applicationPolicy,
certificatePolicy,
verificationTime);
revocationMode,
revocationFlag,
verificationTime,
ref remainingDownloadTime);

if (chain.ChainStatus.Length == 0 && downloaded.Count > 0)
{
Expand All @@ -61,15 +71,6 @@ public static IChainPal BuildChain(
return chain;
}

private static void CheckRevocationMode(X509RevocationMode revocationMode)
{
if (revocationMode != X509RevocationMode.NoCheck)
{
// TODO (#2203): Add support for revocation once networking is ready.
throw new NotImplementedException(SR.WorkInProgress);
}
}

private static void SaveIntermediateCertificates(
X509ChainElement[] chainElements,
List<X509Certificate2> downloaded)
Expand Down
Loading