Пример #1
0
        private static uint HandleEventPeerCertificateReceived(State state, ref ConnectionEvent connectionEvent)
        {
            SslPolicyErrors            sslPolicyErrors        = SslPolicyErrors.None;
            X509Chain?                 chain                  = null;
            X509Certificate2?          certificate            = null;
            X509Certificate2Collection?additionalCertificates = null;

            try
            {
                if (connectionEvent.Data.PeerCertificateReceived.PlatformCertificateHandle != IntPtr.Zero)
                {
                    if (OperatingSystem.IsWindows())
                    {
                        certificate = new X509Certificate2(connectionEvent.Data.PeerCertificateReceived.PlatformCertificateHandle);
                    }
                    else
                    {
                        unsafe
                        {
                            ReadOnlySpan <QuicBuffer> quicBuffer = new ReadOnlySpan <QuicBuffer>((void *)connectionEvent.Data.PeerCertificateReceived.PlatformCertificateHandle, sizeof(QuicBuffer));
                            certificate = new X509Certificate2(new ReadOnlySpan <byte>(quicBuffer[0].Buffer, (int)quicBuffer[0].Length));

                            if (connectionEvent.Data.PeerCertificateReceived.PlatformCertificateChainHandle != IntPtr.Zero)
                            {
                                quicBuffer = new ReadOnlySpan <QuicBuffer>((void *)connectionEvent.Data.PeerCertificateReceived.PlatformCertificateChainHandle, sizeof(QuicBuffer));
                                if (quicBuffer[0].Length != 0 && quicBuffer[0].Buffer != null)
                                {
                                    additionalCertificates = new X509Certificate2Collection();
                                    additionalCertificates.Import(new ReadOnlySpan <byte>(quicBuffer[0].Buffer, (int)quicBuffer[0].Length));
                                }
                            }
                        }
                    }
                }

                if (certificate == null)
                {
                    if (NetEventSource.Log.IsEnabled() && state.RemoteCertificateRequired)
                    {
                        NetEventSource.Error(state, $"{state.TraceId} Remote certificate required, but no remote certificate received");
                    }
                    sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable;
                }
                else
                {
                    chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode = state.RevocationMode;
                    chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
                    chain.ChainPolicy.ApplicationPolicy.Add(state.IsServer ? s_clientAuthOid : s_serverAuthOid);

                    if (additionalCertificates != null && additionalCertificates.Count > 1)
                    {
                        chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
                    }

                    sslPolicyErrors |= CertificateValidation.BuildChainAndVerifyProperties(chain, certificate, true, state.IsServer, state.TargetHost);
                }

                if (!state.RemoteCertificateRequired)
                {
                    sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable;
                }

                state.RemoteCertificate = certificate;

                if (state.RemoteCertificateValidationCallback != null)
                {
                    bool success = state.RemoteCertificateValidationCallback(state, certificate, chain, sslPolicyErrors);
                    // Unset the callback to prevent multiple invocations of the callback per a single connection.
                    // Return the same value as the custom callback just did.
                    state.RemoteCertificateValidationCallback = (_, _, _, _) => success;

                    if (!success && NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(state, $"{state.TraceId} Remote certificate rejected by verification callback");
                    }

                    if (!success)
                    {
                        throw new AuthenticationException(SR.net_quic_cert_custom_validation);
                    }

                    return(MsQuicStatusCodes.Success);
                }

                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(state, $"{state.TraceId} Certificate validation for '${certificate?.Subject}' finished with ${sslPolicyErrors}");
                }


                if (sslPolicyErrors != SslPolicyErrors.None)
                {
                    throw new AuthenticationException(SR.Format(SR.net_quic_cert_chain_validation, sslPolicyErrors));
                }

                return(MsQuicStatusCodes.Success);
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(state, $"{state.TraceId} Certificate validation failed ${ex.Message}");
                }
                throw;
            }
        }
Пример #2
0
        private static unsafe int HandleEventPeerCertificateReceived(State state, ref QUIC_CONNECTION_EVENT connectionEvent)
        {
            SslPolicyErrors            sslPolicyErrors        = SslPolicyErrors.None;
            X509Chain?                 chain                  = null;
            X509Certificate2?          certificate            = null;
            X509Certificate2Collection?additionalCertificates = null;
            IntPtr certificateBuffer = IntPtr.Zero;
            int    certificateLength = 0;

            try
            {
                IntPtr certificateHandle = (IntPtr)connectionEvent.PEER_CERTIFICATE_RECEIVED.Certificate;
                if (certificateHandle != IntPtr.Zero)
                {
                    if (OperatingSystem.IsWindows())
                    {
                        certificate = new X509Certificate2(certificateHandle);
                    }
                    else
                    {
                        unsafe
                        {
                            QUIC_BUFFER *certBuffer = (QUIC_BUFFER *)certificateHandle;
                            certificate       = new X509Certificate2(new ReadOnlySpan <byte>(certBuffer->Buffer, (int)certBuffer->Length));
                            certificateBuffer = (IntPtr)certBuffer->Buffer;
                            certificateLength = (int)certBuffer->Length;

                            IntPtr chainHandle = (IntPtr)connectionEvent.PEER_CERTIFICATE_RECEIVED.Chain;
                            if (chainHandle != IntPtr.Zero)
                            {
                                QUIC_BUFFER *chainBuffer = (QUIC_BUFFER *)chainHandle;
                                if (chainBuffer->Length != 0 && chainBuffer->Buffer != null)
                                {
                                    additionalCertificates = new X509Certificate2Collection();
                                    additionalCertificates.Import(new ReadOnlySpan <byte>(chainBuffer->Buffer, (int)chainBuffer->Length));
                                }
                            }
                        }
                    }
                }

                if (certificate == null)
                {
                    if (NetEventSource.Log.IsEnabled() && state.RemoteCertificateRequired)
                    {
                        NetEventSource.Error(state, $"{state.Handle} Remote certificate required, but no remote certificate received");
                    }
                    sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable;
                }
                else
                {
                    chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode = state.RevocationMode;
                    chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
                    chain.ChainPolicy.ApplicationPolicy.Add(state.IsServer ? s_clientAuthOid : s_serverAuthOid);

                    if (additionalCertificates != null && additionalCertificates.Count > 1)
                    {
                        chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
                    }

                    sslPolicyErrors |= CertificateValidation.BuildChainAndVerifyProperties(chain, certificate, true, state.IsServer, state.TargetHost, certificateBuffer, certificateLength);
                }

                if (!state.RemoteCertificateRequired)
                {
                    sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable;
                }

                state.RemoteCertificate = certificate;

                if (state.RemoteCertificateValidationCallback != null)
                {
                    bool success = state.RemoteCertificateValidationCallback(state, certificate, chain, sslPolicyErrors);
                    // Unset the callback to prevent multiple invocations of the callback per a single connection.
                    // Return the same value as the custom callback just did.
                    state.RemoteCertificateValidationCallback = (_, _, _, _) => success;

                    if (!success && NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(state, $"{state.Handle} Remote certificate rejected by verification callback");
                    }

                    if (!success)
                    {
                        if (state.IsServer)
                        {
                            return(QUIC_STATUS_USER_CANCELED);
                        }

                        throw new AuthenticationException(SR.net_quic_cert_custom_validation);
                    }

                    return(QUIC_STATUS_SUCCESS);
                }

                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(state, $"{state.Handle} Certificate validation for '${certificate?.Subject}' finished with ${sslPolicyErrors}");
                }


                if (sslPolicyErrors != SslPolicyErrors.None)
                {
                    if (state.IsServer)
                    {
                        return(QUIC_STATUS_HANDSHAKE_FAILURE);
                    }

                    throw new AuthenticationException(SR.Format(SR.net_quic_cert_chain_validation, sslPolicyErrors));
                }

                return(QUIC_STATUS_SUCCESS);
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(state, $"{state.Handle} Certificate validation failed ${ex.Message}");
                }
                throw;
            }
        }
Пример #3
0
            private static bool VerifyCertChain(SafeX509StoreCtxHandle storeCtx, EasyRequest easy)
            {
                IntPtr leafCertPtr = Interop.Crypto.X509StoreCtxGetTargetCert(storeCtx);

                if (leafCertPtr == IntPtr.Zero)
                {
                    EventSourceTrace("Invalid certificate pointer", easy: easy);
                    return(false);
                }

                X509Certificate2[] otherCerts = null;
                int otherCertsCount           = 0;
                var leafCert = new X509Certificate2(leafCertPtr);

                try
                {
                    // 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.ServerCertificateCustomValidationCallback == 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(true);
                        }

                        // 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
                    // or certificate revocation checking was enabled. 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).
                    using (var 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
                            return(errors == SslPolicyErrors.None);
                        }
                        else
                        {
                            // Authenticate the remote party: (e.g. when operating in client mode, authenticate the server).
                            chain.ChainPolicy.ApplicationPolicy.Add(s_serverAuthOid);

                            SslPolicyErrors errors = CertificateValidation.BuildChainAndVerifyProperties(chain, leafCert,
                                                                                                         checkCertName: true, hostName: easy._requestMessage.RequestUri.Host); // we disabled automatic host verification, so we do it here
                            return(serverCallback(easy._requestMessage, leafCert, chain, errors));
                        }
                    }
                }
                finally
                {
                    for (int i = 0; i < otherCertsCount; i++)
                    {
                        otherCerts[i].Dispose();
                    }
                    leafCert.Dispose();
                }
            }
Пример #4
0
            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");
                        return(0);
                    }

                    using (X509Certificate2 leafCert = new X509Certificate2(leafCertPtr))
                    {
                        // Set up the CBT with this certificate.
                        easy._requestContentStream?.SetChannelBindingToken(leafCert);

                        // 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.FailRequest(exc);
                                    success = false;
                                }
                            }
                        }

                        for (int i = 0; i < otherCertsCount; i++)
                        {
                            otherCerts[i].Dispose();
                        }

                        return(success ? 1 : 0);
                    }
                }
            }