Skip to content

Commit fa291e1

Browse files
authored
Merge pull request #1246 from Unity-Technologies/unitytls/custom-chain
Custom tls chain impl to fix callback issue and avoid copying
2 parents 41eea25 + b63fa29 commit fa291e1

File tree

5 files changed

+149
-50
lines changed

5 files changed

+149
-50
lines changed

mcs/class/System/Mono.UnityTls/CertHelper.cs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,6 @@ public static void AddCertificateToNativeChain (UnityTls.unitytls_x509list* nati
3131
}
3232
}
3333
}
34-
35-
public static X509CertificateCollection NativeChainToManagedCollection (UnityTls.unitytls_x509list_ref nativeCertificateChain, UnityTls.unitytls_errorstate* errorState)
36-
{
37-
X509CertificateCollection certificates = new X509CertificateCollection ();
38-
39-
var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, (size_t)0, errorState);
40-
for (int i = 0; cert.handle != UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; ++i) {
41-
size_t certBufferSize = UnityTls.NativeInterface.unitytls_x509_export_der (cert, null, (size_t)0, errorState);
42-
var certBuffer = new byte[(int)certBufferSize]; // Need to reallocate every time since X509Certificate constructor takes no length but only a byte array.
43-
fixed(byte* certBufferPtr = certBuffer) {
44-
UnityTls.NativeInterface.unitytls_x509_export_der (cert, certBufferPtr, certBufferSize, errorState);
45-
}
46-
certificates.Add (new X509Certificate (certBuffer));
47-
48-
cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, (size_t)i, errorState);
49-
}
50-
51-
return certificates;
52-
}
5334
}
5435
}
5536
#endif

mcs/class/System/Mono.UnityTls/UnityTlsContext.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,17 @@ static private UnityTls.unitytls_x509verify_result VerifyCallback (void* userDat
446446
private UnityTls.unitytls_x509verify_result VerifyCallback (UnityTls.unitytls_x509list_ref chain, UnityTls.unitytls_errorstate* errorState)
447447
{
448448
try {
449-
X509CertificateCollection certificates = CertHelper.NativeChainToManagedCollection (chain, errorState);
450-
remoteCertificate = new X509Certificate (certificates [0]);
451-
452-
if (ValidateCertificate (certificates))
453-
return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS;
454-
else
455-
return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED;
449+
using (var chainImpl = new X509ChainImplUnityTls(chain))
450+
using (var managedChain = new X509Chain (chainImpl)) {
451+
remoteCertificate = managedChain.ChainElements[0].Certificate;
452+
453+
// Note that the overload of this method that takes a X509CertificateCollection will not pass on the chain to
454+
// user callbacks like ServicePointManager.ServerCertificateValidationCallback which can cause issues along the line.
455+
if (ValidateCertificate (remoteCertificate, managedChain))
456+
return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS;
457+
else
458+
return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED;
459+
}
456460
} catch (Exception ex) { // handle all exceptions and store them for later since we don't want to let them go through native code.
457461
if (lastException == null)
458462
lastException = ex;

mcs/class/System/Mono.UnityTls/UnityTlsProvider.cs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,26 @@ internal override bool ValidateCertificate (
5353
X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
5454
ref MonoSslPolicyErrors errors, ref int status11)
5555
{
56-
if (certificates == null) {
57-
errors |= MonoSslPolicyErrors.RemoteCertificateNotAvailable;
58-
return false;
59-
}
56+
var errorState = UnityTls.NativeInterface.unitytls_errorstate_create ();
6057

61-
if (wantsChain)
62-
chain = MNS.SystemCertificateValidator.CreateX509Chain (certificates);
58+
var unityTlsChainImpl = chain.Impl as X509ChainImplUnityTls;
59+
if (unityTlsChainImpl == null)
60+
{
61+
if (certificates == null || certificates.Count == 0) {
62+
errors |= MonoSslPolicyErrors.RemoteCertificateNotAvailable;
63+
return false;
64+
}
6365

64-
if (certificates == null || certificates.Count == 0) {
65-
errors |= MonoSslPolicyErrors.RemoteCertificateNotAvailable;
66-
return false;
66+
if (wantsChain)
67+
chain = MNS.SystemCertificateValidator.CreateX509Chain (certificates);
68+
}
69+
else
70+
{
71+
var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (unityTlsChainImpl.NativeCertificateChain, (size_t)0, &errorState);
72+
if (cert.handle == UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE) {
73+
errors |= MonoSslPolicyErrors.RemoteCertificateNotAvailable;
74+
return false;
75+
}
6776
}
6877

6978
// fixup targetHost name by removing port
@@ -73,10 +82,9 @@ internal override bool ValidateCertificate (
7382
targetHost = targetHost.Substring (0, pos);
7483
}
7584

76-
// convert cert to native
77-
var errorState = UnityTls.NativeInterface.unitytls_errorstate_create ();
78-
var certificatesNative = UnityTls.NativeInterface.unitytls_x509list_create (&errorState);
85+
// convert cert to native or extract from unityTlsChainImpl.
7986
var result = UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_NOT_DONE;
87+
UnityTls.unitytls_x509list* certificatesNative = null;
8088
try
8189
{
8290
// Things the validator provides that we might want to make use of here:
@@ -85,28 +93,40 @@ internal override bool ValidateCertificate (
8593
//validator.Settings.CertificateValidationTime
8694
//validator.Settings.CertificateSearchPaths // currently only used by MonoBtlsProvider
8795

88-
CertHelper.AddCertificatesToNativeChain (certificatesNative, certificates, &errorState);
89-
var certificatesNativeRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (certificatesNative, &errorState);
96+
UnityTls.unitytls_x509list_ref certificatesNativeRef;
97+
if (unityTlsChainImpl == null)
98+
{
99+
certificatesNative = UnityTls.NativeInterface.unitytls_x509list_create (&errorState);
100+
CertHelper.AddCertificatesToNativeChain (certificatesNative, certificates, &errorState);
101+
certificatesNativeRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (certificatesNative, &errorState);
102+
}
103+
else
104+
certificatesNativeRef = unityTlsChainImpl.NativeCertificateChain;
105+
90106
var targetHostUtf8 = Encoding.UTF8.GetBytes (targetHost);
91107

92108
if (validator.Settings.TrustAnchors != null) {
93-
var trustCAnative = UnityTls.NativeInterface.unitytls_x509list_create (&errorState);
94-
CertHelper.AddCertificatesToNativeChain (trustCAnative, validator.Settings.TrustAnchors, &errorState);
95-
var trustCAnativeRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (certificatesNative, &errorState);
96-
97-
fixed (byte* targetHostUtf8Ptr = targetHostUtf8) {
98-
result = UnityTls.NativeInterface.unitytls_x509verify_explicit_ca (certificatesNativeRef, trustCAnativeRef, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null, &errorState);
109+
UnityTls.unitytls_x509list* trustCAnative = null;
110+
try
111+
{
112+
trustCAnative = UnityTls.NativeInterface.unitytls_x509list_create (&errorState);
113+
CertHelper.AddCertificatesToNativeChain (trustCAnative, validator.Settings.TrustAnchors, &errorState);
114+
var trustCAnativeRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (trustCAnative, &errorState);
115+
116+
fixed (byte* targetHostUtf8Ptr = targetHostUtf8) {
117+
result = UnityTls.NativeInterface.unitytls_x509verify_explicit_ca (certificatesNativeRef, trustCAnativeRef, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null, &errorState);
118+
}
119+
}
120+
finally {
121+
UnityTls.NativeInterface.unitytls_x509list_free (trustCAnative);
99122
}
100-
101-
UnityTls.NativeInterface.unitytls_x509list_free (trustCAnative);
102123
} else {
103124
fixed (byte* targetHostUtf8Ptr = targetHostUtf8) {
104125
result = UnityTls.NativeInterface.unitytls_x509verify_default_ca (certificatesNativeRef, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null, &errorState);
105126
}
106127
}
107128
}
108-
finally
109-
{
129+
finally {
110130
UnityTls.NativeInterface.unitytls_x509list_free (certificatesNative);
111131
}
112132

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#if SECURITY_DEP
2+
3+
using System;
4+
using System.Text;
5+
using System.Security;
6+
using System.Security.Cryptography;
7+
using System.Security.Cryptography.X509Certificates;
8+
using size_t = System.IntPtr;
9+
10+
namespace Mono.Unity
11+
{
12+
// Follows mostly X509ChainImplBtls
13+
class X509ChainImplUnityTls : X509ChainImpl
14+
{
15+
X509ChainElementCollection elements;
16+
UnityTls.unitytls_x509list_ref nativeCertificateChain;
17+
X509ChainPolicy policy = new X509ChainPolicy ();
18+
19+
internal X509ChainImplUnityTls (UnityTls.unitytls_x509list_ref nativeCertificateChain)
20+
{
21+
this.elements = null;
22+
this.nativeCertificateChain = nativeCertificateChain;
23+
}
24+
25+
public override bool IsValid {
26+
get { return nativeCertificateChain.handle != UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; }
27+
}
28+
29+
public override IntPtr Handle {
30+
get { return new IntPtr((long)nativeCertificateChain.handle); }
31+
}
32+
33+
internal UnityTls.unitytls_x509list_ref NativeCertificateChain => nativeCertificateChain;
34+
35+
public override X509ChainElementCollection ChainElements {
36+
get {
37+
ThrowIfContextInvalid ();
38+
if (elements != null)
39+
return elements;
40+
41+
unsafe
42+
{
43+
elements = new X509ChainElementCollection ();
44+
UnityTls.unitytls_errorstate errorState = UnityTls.NativeInterface.unitytls_errorstate_create ();
45+
var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, (size_t)0, &errorState);
46+
for (int i = 0; cert.handle != UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; ++i) {
47+
size_t certBufferSize = UnityTls.NativeInterface.unitytls_x509_export_der (cert, null, (size_t)0, &errorState);
48+
var certBuffer = new byte[(int)certBufferSize]; // Need to reallocate every time since X509Certificate constructor takes no length but only a byte array.
49+
fixed(byte* certBufferPtr = certBuffer) {
50+
UnityTls.NativeInterface.unitytls_x509_export_der (cert, certBufferPtr, certBufferSize, &errorState);
51+
}
52+
elements.Add (new X509Certificate2 (certBuffer));
53+
54+
cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, (size_t)i, &errorState);
55+
}
56+
}
57+
58+
return elements;
59+
}
60+
}
61+
62+
public override X509ChainPolicy ChainPolicy {
63+
get { return policy; }
64+
set { policy = value; }
65+
}
66+
67+
public override X509ChainStatus[] ChainStatus {
68+
get { throw new NotImplementedException (); }
69+
}
70+
71+
public override bool Build (X509Certificate2 certificate)
72+
{
73+
return false;
74+
}
75+
76+
public override void Reset ()
77+
{
78+
if (elements != null) {
79+
nativeCertificateChain.handle = UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE;
80+
elements.Clear ();
81+
elements = null;
82+
}
83+
}
84+
85+
protected override void Dispose (bool disposing)
86+
{
87+
Reset();
88+
base.Dispose (disposing);
89+
}
90+
}
91+
}
92+
93+
#endif

mcs/class/System/common.sources

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ Mono.UnityTls/UnityTlsProvider.cs
261261
Mono.UnityTls/UnityTlsStream.cs
262262
Mono.UnityTls/UnityTlsContext.cs
263263
Mono.UnityTls/UnityTlsConversions.cs
264+
Mono.UnityTls/X509ChainImplUnityTls.cs
264265
Mono.UnityTls/Debug.cs
265266
Mono.UnityTls/CertHelper.cs
266267

0 commit comments

Comments
 (0)