private static extern int SetX509ChainVerifyTime( SafeX509StoreCtxHandle ctx, int year, int month, int day, int hour, int minute, int second, [MarshalAs(UnmanagedType.Bool)] bool isDst);
private static int VerifyCertChain(IntPtr storeCtxPtr, IntPtr arg) { List<X509Certificate2> otherCerts; bool success; using (SafeX509StoreCtxHandle storeCtx = new SafeX509StoreCtxHandle(storeCtxPtr, ownsHandle: false)) using (X509Chain chain = new X509Chain()) { chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; IntPtr leafCertPtr = Interop.Crypto.X509StoreCtxGetTargetCert(storeCtx); if (IntPtr.Zero == leafCertPtr) { Debug.Fail("Invalid target certificate"); return -1; } using (SafeSharedX509StackHandle extraStack = Interop.Crypto.X509StoreCtxGetSharedUntrusted(storeCtx)) { int extraSize = extraStack.IsInvalid ? 0 : Interop.Crypto.GetX509StackFieldCount(extraStack); otherCerts = new List<X509Certificate2>(extraSize); for (int i = 0; i < extraSize; i++) { IntPtr certPtr = Interop.Crypto.GetX509StackField(extraStack, i); if (certPtr != IntPtr.Zero) { X509Certificate2 cert = new X509Certificate2(certPtr); otherCerts.Add(cert); chain.ChainPolicy.ExtraStore.Add(cert); } } } using (X509Certificate2 leafCert = new X509Certificate2(leafCertPtr)) { success = chain.Build(leafCert); } } foreach (X509Certificate2 otherCert in otherCerts) { otherCert.Dispose(); } return success ? 1 : 0; }
internal int VerifyCallback(int ok, IntPtr ctx) { if (ok < 0) { return ok; } try { using (var storeCtx = new SafeX509StoreCtxHandle(ctx, ownsHandle: false)) { Interop.Crypto.X509VerifyStatusCode errorCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); int errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx); if (errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { while (Errors.Count <= errorDepth) { Errors.Add(null); } if (Errors[errorDepth] == null) { Errors[errorDepth] = new List<Interop.Crypto.X509VerifyStatusCode>(); } Errors[errorDepth].Add(errorCode); } } return 1; } catch { return -1; } }
internal static extern void X509StoreCtxSetVerifyCallback(SafeX509StoreCtxHandle ctx, X509StoreVerifyCallback callback);
internal static extern int X509StoreCtxGetErrorDepth(SafeX509StoreCtxHandle ctx);
internal static extern X509VerifyStatusCode X509StoreCtxGetError(SafeX509StoreCtxHandle ctx);
internal int VerifyCallback(int ok, IntPtr ctx) { if (ok < 0) { return ok; } try { using (var storeCtx = new SafeX509StoreCtxHandle(ctx, ownsHandle: false)) { Interop.Crypto.X509VerifyStatusCode errorCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); int errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx); // We don't report "OK" as an error. // For compatibility with Windows / .NET Framework, do not report X509_V_CRL_NOT_YET_VALID. if (errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_OK && errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID) { while (Errors.Count <= errorDepth) { Errors.Add(null); } if (Errors[errorDepth] == null) { Errors[errorDepth] = new List<Interop.Crypto.X509VerifyStatusCode>(); } Errors[errorDepth].Add(errorCode); } } return 1; } catch { return -1; } }
internal static extern IntPtr X509StoreCtxGetTargetCert(SafeX509StoreCtxHandle ctx);
private static int VerifyCertChain(IntPtr storeCtxPtr, IntPtr arg) { using (SafeX509StoreCtxHandle storeCtx = new SafeX509StoreCtxHandle(storeCtxPtr, ownsHandle: false)) { // First use the default verification provided directly by OpenSSL. // If it succeeds in verifying the cert chain, we're done. // (Employing this instead of our custom implementation will need to be // revisited if we ever decide to a) introduce a "disallowed" store // that enables users to "untrust" certs the system trusts, or b) decide // CRL checking is required, neither of which is done by OpenSSL). int sslResult = Interop.Crypto.X509VerifyCert(storeCtx); if (sslResult == 1) { return 1; } // X509_verify_cert can return < 0 in the case of programmer error Debug.Assert(sslResult == 0, "Unexpected error from X509_verify_cert: " + sslResult); // Only if the fast default verification fails do we then fall back to our more // manual and more expensive verification that includes checking the user's // certs and not just the system store ones. List<X509Certificate2> otherCerts; bool success; using (X509Chain chain = new X509Chain()) { chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; IntPtr leafCertPtr = Interop.Crypto.X509StoreCtxGetTargetCert(storeCtx); if (IntPtr.Zero == leafCertPtr) { Debug.Fail("Invalid target certificate"); return -1; } using (SafeSharedX509StackHandle extraStack = Interop.Crypto.X509StoreCtxGetSharedUntrusted(storeCtx)) { int extraSize = extraStack.IsInvalid ? 0 : Interop.Crypto.GetX509StackFieldCount(extraStack); otherCerts = new List<X509Certificate2>(extraSize); for (int i = 0; i < extraSize; i++) { IntPtr certPtr = Interop.Crypto.GetX509StackField(extraStack, i); if (certPtr != IntPtr.Zero) { X509Certificate2 cert = new X509Certificate2(certPtr); otherCerts.Add(cert); chain.ChainPolicy.ExtraStore.Add(cert); } } } using (X509Certificate2 leafCert = new X509Certificate2(leafCertPtr)) { success = chain.Build(leafCert); AddChannelBindingToken(leafCert, arg); } } foreach (X509Certificate2 otherCert in otherCerts) { otherCert.Dispose(); } return success ? 1 : 0; } }
internal static extern SafeX509StackHandle X509_STORE_CTX_get1_chain(SafeX509StoreCtxHandle ctx);
internal static extern int X509_verify_cert(SafeX509StoreCtxHandle ctx);
internal static extern bool X509_STORE_CTX_init(SafeX509StoreCtxHandle ctx, SafeX509StoreHandle store, SafeX509Handle x509, IntPtr zero);
internal static extern int X509_STORE_CTX_get_error_depth(SafeX509StoreCtxHandle ctx);
internal static extern X509VerifyStatusCode X509_STORE_CTX_get_error(SafeX509StoreCtxHandle ctx);
private static int VerifyClientCertificate(int preverify_ok, IntPtr x509_ctx_ptr) { using (SafeX509StoreCtxHandle storeHandle = new SafeX509StoreCtxHandle(x509_ctx_ptr, false)) { using (var chain = new X509Chain()) { chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; using (SafeX509StackHandle chainStack = Crypto.X509StoreCtxGetChain(storeHandle)) { if (chainStack.IsInvalid) { Debug.Fail("Invalid chain stack handle"); return 0; } IntPtr certPtr = Crypto.GetX509StackField(chainStack, 0); if (IntPtr.Zero == certPtr) { return 0; } using (X509Certificate2 cert = new X509Certificate2(certPtr)) { return chain.Build(cert) ? 1 : 0; } } } } }
internal static extern SafeX509StackHandle X509StoreCtxGetChain(SafeX509StoreCtxHandle ctx);
private static extern SafeSharedX509StackHandle X509StoreCtxGetSharedUntrusted_private(SafeX509StoreCtxHandle ctx);
internal static extern bool X509StoreCtxInit(SafeX509StoreCtxHandle ctx, SafeX509StoreHandle store, SafeX509Handle x509);
internal static SafeSharedX509StackHandle X509StoreCtxGetSharedUntrusted(SafeX509StoreCtxHandle ctx) { return SafeInteriorHandle.OpenInteriorHandle( x => X509StoreCtxGetSharedUntrusted_private(x), ctx); }
internal static extern int X509VerifyCert(SafeX509StoreCtxHandle ctx);
private static int VerifyCertChain(IntPtr storeCtxPtr, IntPtr curlPtr) { EasyRequest easy; if (!TryGetEasyRequest(curlPtr, out easy)) { EventSourceTrace("Could not find associated easy request: {0}", curlPtr); return 0; } using (var storeCtx = new SafeX509StoreCtxHandle(storeCtxPtr, ownsHandle: false)) { IntPtr leafCertPtr = Interop.Crypto.X509StoreCtxGetTargetCert(storeCtx); if (IntPtr.Zero == leafCertPtr) { EventSourceTrace("Invalid certificate pointer", easy: easy); return 0; } using (X509Certificate2 leafCert = new X509Certificate2(leafCertPtr)) { // We need to respect the user's server validation callback if there is one. If there isn't one, // we can start by first trying to use OpenSSL's verification, though only if CRL checking is disabled, // as OpenSSL doesn't do that. if (easy._handler.ServerCertificateValidationCallback == null && !easy._handler.CheckCertificateRevocationList) { // Start by using the default verification provided directly by OpenSSL. // If it succeeds in verifying the cert chain, we're done. Employing this instead of // our custom implementation will need to be revisited if we ever decide to introduce a // "disallowed" store that enables users to "untrust" certs the system trusts. int sslResult = Interop.Crypto.X509VerifyCert(storeCtx); if (sslResult == 1) { return 1; } // X509_verify_cert can return < 0 in the case of programmer error Debug.Assert(sslResult == 0, "Unexpected error from X509_verify_cert: " + sslResult); } // Either OpenSSL verification failed, or there was a server validation callback. // Either way, fall back to manual and more expensive verification that includes // checking the user's certs (not just the system store ones as OpenSSL does). X509Certificate2[] otherCerts; int otherCertsCount = 0; bool success; using (X509Chain chain = new X509Chain()) { chain.ChainPolicy.RevocationMode = easy._handler.CheckCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; using (SafeSharedX509StackHandle extraStack = Interop.Crypto.X509StoreCtxGetSharedUntrusted(storeCtx)) { if (extraStack.IsInvalid) { otherCerts = Array.Empty<X509Certificate2>(); } else { int extraSize = Interop.Crypto.GetX509StackFieldCount(extraStack); otherCerts = new X509Certificate2[extraSize]; for (int i = 0; i < extraSize; i++) { IntPtr certPtr = Interop.Crypto.GetX509StackField(extraStack, i); if (certPtr != IntPtr.Zero) { X509Certificate2 cert = new X509Certificate2(certPtr); otherCerts[otherCertsCount++] = cert; chain.ChainPolicy.ExtraStore.Add(cert); } } } } var serverCallback = easy._handler._serverCertificateValidationCallback; if (serverCallback == null) { SslPolicyErrors errors = CertificateValidation.BuildChainAndVerifyProperties(chain, leafCert, checkCertName: false, hostName: null); // libcurl already verifies the host name success = errors == SslPolicyErrors.None; } else { SslPolicyErrors errors = CertificateValidation.BuildChainAndVerifyProperties(chain, leafCert, checkCertName: true, hostName: easy._requestMessage.RequestUri.Host); // we disabled automatic host verification, so we do it here try { success = serverCallback(easy._requestMessage, leafCert, chain, errors); } catch (Exception exc) { EventSourceTrace("Server validation callback threw exception: {0}", exc, easy: easy); easy.FailRequest(exc); success = false; } } } for (int i = 0; i < otherCertsCount; i++) { otherCerts[i].Dispose(); } return success ? 1 : 0; } } }
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(); } int succeeded = SetX509ChainVerifyTime( ctx, verifyTime.Year, verifyTime.Month, verifyTime.Day, verifyTime.Hour, verifyTime.Minute, verifyTime.Second, verifyTime.IsDaylightSavingTime()); if (succeeded != 1) { throw new CryptographicException(); } }