Fixed: Mono not validating cross-signed certficates properly
This commit is contained in:
parent
0c05236bee
commit
449c1caf55
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace NzbDrone.RuntimePatches.Mono
|
||||
{
|
||||
// Mono 5.x - 6.x bug 19886
|
||||
// The BoringTLS provider does not enable the trust-first option that's default on in openssl 1.1.0 and up.
|
||||
// This prevents it from building the short trusted chain and errors out on old (expired) chains included in the certificate.
|
||||
// This is a problem with Cross-Signed certificates that have an expired legacy root signing the new root.
|
||||
// The Flags default is 0, while X509_V_FLAG_TRUSTED_FIRST is 0x8000.
|
||||
// There's no way to override the default flags via an option in mono so we have to hook in.
|
||||
public class BoringTLSVerifyFlagsPatch : MonoRuntimePatchBase
|
||||
{
|
||||
private static BoringTLSVerifyFlagsPatch Instance;
|
||||
|
||||
public override Version MonoMinVersion => new Version(5, 0);
|
||||
public override Version MonoMaxVersion => new Version(6, 10);
|
||||
|
||||
protected override void Patch()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
TryPatchMethod("Mono.Btls.MonoBtlsX509VerifyParam, System", "GetSslServer");
|
||||
}
|
||||
|
||||
// We need a Transpiler coz MonoBtlsX509VerifyParam is non-public
|
||||
// Note that MonoBtlsProvider.GetVerifyParam would be a more 'correct' patch location
|
||||
|
||||
// public static MonoBtlsX509VerifyParam GetSslServer ()
|
||||
// {
|
||||
// - return Lookup("ssl_server", true);
|
||||
// + var orig = Lookup("ssl_server", true);
|
||||
// + var copy = orig.Copy();
|
||||
// + orig.Dispose();
|
||||
// + copy.SetFlags(0x8000);
|
||||
// + return copy;
|
||||
// }
|
||||
static IEnumerable<CodeInstruction> Transpiler_GetSslServer(IEnumerable<CodeInstruction> instructions, MethodBase method, ILGenerator generator)
|
||||
{
|
||||
var codes = instructions.ToList();
|
||||
|
||||
var patchable = codes.Matches(OpCodes.Ldstr, OpCodes.Ldc_I4_1, OpCodes.Call, OpCodes.Ret);
|
||||
|
||||
var targetType = method.DeclaringType;
|
||||
var copyMethod = targetType.GetMethod("Copy");
|
||||
var disposeMethod = targetType.GetMethod("Dispose");
|
||||
var setFlagsMethod = targetType.GetMethod("SetFlags");
|
||||
|
||||
if (patchable && copyMethod != null && disposeMethod != null && setFlagsMethod != null)
|
||||
{
|
||||
var copy = generator.DeclareLocal(targetType);
|
||||
|
||||
// Remove Ret
|
||||
codes.RemoveAt(codes.Count - 1);
|
||||
|
||||
codes.Add(new CodeInstruction(OpCodes.Dup));
|
||||
codes.Add(new CodeInstruction(OpCodes.Call, copyMethod)); // Copy the readonly original
|
||||
codes.Add(new CodeInstruction(OpCodes.Stloc, copy));
|
||||
codes.Add(new CodeInstruction(OpCodes.Callvirt, disposeMethod)); // Dispose the original
|
||||
codes.Add(new CodeInstruction(OpCodes.Ldloc, copy));
|
||||
codes.Add(new CodeInstruction(OpCodes.Dup));
|
||||
codes.Add(new CodeInstruction(OpCodes.Ldc_I4, 0x8000)); // X509_V_FLAG_TRUSTED_FIRST
|
||||
codes.Add(new CodeInstruction(OpCodes.Call, setFlagsMethod)); // SetFlags is an or-operation
|
||||
codes.Add(new CodeInstruction(OpCodes.Ret));
|
||||
|
||||
Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected");
|
||||
}
|
||||
|
||||
return codes;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue