Exemplo n.º 1
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions authOptions)
            : base(credential)
        {
            Debug.Assert((credential != null) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                _sslContext = CreateSslContext(credential);
                InitializeSslContext(_sslContext, credential, authOptions);
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }
Exemplo n.º 2
0
        // This is helper function to adjust requested protocols based on CipherSuitePolicy and system capability.
        private static SslProtocols CalculateEffectiveProtocols(SslAuthenticationOptions sslAuthenticationOptions)
        {
            // make sure low bit is not set since we use it in context dictionary to distinguish use with ALPN
            Debug.Assert(((int)sslAuthenticationOptions.EnabledSslProtocols & 1) == 0);
            SslProtocols protocols = sslAuthenticationOptions.EnabledSslProtocols & ~((SslProtocols)1);

            if (!Interop.Ssl.Capabilities.Tls13Supported)
            {
                if (protocols != SslProtocols.None &&
                    CipherSuitesPolicyPal.WantsTls13(protocols))
                {
                    protocols = protocols & (~SslProtocols.Tls13);
                }
            }
            else if (CipherSuitesPolicyPal.WantsTls13(protocols) &&
                     CipherSuitesPolicyPal.ShouldOptOutOfTls13(sslAuthenticationOptions.CipherSuitesPolicy, sslAuthenticationOptions.EncryptionPolicy))
            {
                if (protocols == SslProtocols.None)
                {
                    // we are using default settings but cipher suites policy says that TLS 1.3
                    // is not compatible with our settings (i.e. we requested no encryption or disabled
                    // all TLS 1.3 cipher suites)
                    protocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
                }
                else
                {
                    // user explicitly asks for TLS 1.3 but their policy is not compatible with TLS 1.3
                    throw new SslException(
                              SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                }
            }

            if (CipherSuitesPolicyPal.ShouldOptOutOfLowerThanTls13(sslAuthenticationOptions.CipherSuitesPolicy, sslAuthenticationOptions.EncryptionPolicy))
            {
                if (!CipherSuitesPolicyPal.WantsTls13(protocols))
                {
                    // We cannot provide neither TLS 1.3 or non TLS 1.3, user disabled all cipher suites
                    throw new SslException(
                              SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                }

                protocols = SslProtocols.Tls13;
            }

            return(protocols);
        }
Exemplo n.º 3
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
            : base(credential)
        {
            Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                unsafe
                {
                    _readCallback  = ReadFromConnection;
                    _writeCallback = WriteToConnection;
                }

                _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer);

                int osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
                    _sslContext,
                    _readCallback,
                    _writeCallback);

                if (osStatus != 0)
                {
                    throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null)
                {
                    // On OSX coretls supports only client side. For server, we will silently ignore the option.
                    if (!sslAuthenticationOptions.IsServer)
                    {
                        Interop.AppleCrypto.SslCtxSetAlpnProtos(_sslContext, sslAuthenticationOptions.ApplicationProtocols);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }
Exemplo n.º 4
0
        internal static void UpdateClientCertiticate(SafeSslHandle ssl, SslAuthenticationOptions sslAuthenticationOptions)
        {
            // Disable certificate selection callback. We either got certificate or we will try to proceed without it.
            Interop.Ssl.SslSetClientCertCallback(ssl, 0);

            if (sslAuthenticationOptions.CertificateContext == null)
            {
                return;
            }

            var               credential    = new SafeFreeSslCredentials(sslAuthenticationOptions.CertificateContext, sslAuthenticationOptions.EnabledSslProtocols, sslAuthenticationOptions.EncryptionPolicy, sslAuthenticationOptions.IsServer);
            SafeX509Handle?   certHandle    = credential.CertHandle;
            SafeEvpPKeyHandle?certKeyHandle = credential.CertKeyHandle;

            Debug.Assert(certHandle != null);
            Debug.Assert(certKeyHandle != null);

            int retVal = Ssl.SslUseCertificate(ssl, certHandle);

            if (1 != retVal)
            {
                throw CreateSslException(SR.net_ssl_use_cert_failed);
            }

            retVal = Ssl.SslUsePrivateKey(ssl, certKeyHandle);
            if (1 != retVal)
            {
                throw CreateSslException(SR.net_ssl_use_private_key_failed);
            }

            if (sslAuthenticationOptions.CertificateContext.IntermediateCertificates.Length > 0)
            {
                if (!Ssl.AddExtraChainCertificates(ssl, sslAuthenticationOptions.CertificateContext.IntermediateCertificates))
                {
                    throw CreateSslException(SR.net_ssl_use_cert_failed);
                }
            }
        }
Exemplo n.º 5
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions authOptions)
            : base(credential)
        {
            Debug.Assert((credential != null) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                unsafe
                {
                    _readCallback  = ReadFromConnection;
                    _writeCallback = WriteToConnection;
                }

                _sslContext = CreateSslContext(credential);
                InitializeSslContext(_sslContext, _readCallback, _writeCallback, credential, authOptions);
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }
Exemplo n.º 6
0
        internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle?certHandle, SafeEvpPKeyHandle?certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle?context = null;

            // Always use SSLv23_method, regardless of protocols.  It supports negotiating to the highest
            // mutually supported version and can thus handle any of the set protocols, and we then use
            // SetProtocolOptions to ensure we only allow the ones requested.
            using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method))
            {
                if (innerContext.IsInvalid)
                {
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                if (!Interop.Ssl.Tls13Supported)
                {
                    if (protocols != SslProtocols.None &&
                        CipherSuitesPolicyPal.WantsTls13(protocols))
                    {
                        protocols = protocols & (~SslProtocols.Tls13);
                    }
                }
                else if (CipherSuitesPolicyPal.WantsTls13(protocols) &&
                         CipherSuitesPolicyPal.ShouldOptOutOfTls13(sslAuthenticationOptions.CipherSuitesPolicy, policy))
                {
                    if (protocols == SslProtocols.None)
                    {
                        // we are using default settings but cipher suites policy says that TLS 1.3
                        // is not compatible with our settings (i.e. we requested no encryption or disabled
                        // all TLS 1.3 cipher suites)
                        protocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
                    }
                    else
                    {
                        // user explicitly asks for TLS 1.3 but their policy is not compatible with TLS 1.3
                        throw new SslException(
                                  SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                    }
                }

                if (CipherSuitesPolicyPal.ShouldOptOutOfLowerThanTls13(sslAuthenticationOptions.CipherSuitesPolicy, policy))
                {
                    if (!CipherSuitesPolicyPal.WantsTls13(protocols))
                    {
                        // We cannot provide neither TLS 1.3 or non TLS 1.3, user disabled all cipher suites
                        throw new SslException(
                                  SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                    }

                    protocols = SslProtocols.Tls13;
                }

                // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just
                // create the handle, it's rooted by the using, no one else has a reference to it, etc.
                Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols);

                // Sets policy and security level
                if (!Ssl.SetEncryptionPolicy(innerContext, policy))
                {
                    throw new SslException(
                              SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                }

                // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet
                // shutdown (we aren't negotiating for session close to enable later session
                // restoration).
                //
                // If you find yourself wanting to remove this line to enable bidirectional
                // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect().
                // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
                Ssl.SslCtxSetQuietShutdown(innerContext);

                byte[]? cipherList =
                    CipherSuitesPolicyPal.GetOpenSslCipherList(sslAuthenticationOptions.CipherSuitesPolicy, protocols, policy);

                Debug.Assert(cipherList == null || (cipherList.Length >= 1 && cipherList[cipherList.Length - 1] == 0));

                byte[]? cipherSuites =
                    CipherSuitesPolicyPal.GetOpenSslCipherSuites(sslAuthenticationOptions.CipherSuitesPolicy, protocols, policy);

                Debug.Assert(cipherSuites == null || (cipherSuites.Length >= 1 && cipherSuites[cipherSuites.Length - 1] == 0));

                unsafe
                {
                    fixed(byte *cipherListStr = cipherList)
                    fixed(byte *cipherSuitesStr = cipherSuites)
                    {
                        if (!Ssl.SetCiphers(innerContext, cipherListStr, cipherSuitesStr))
                        {
                            Crypto.ErrClearError();
                            throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                        }
                    }
                }

                bool hasCertificateAndKey =
                    certHandle != null && !certHandle.IsInvalid &&
                    certKeyHandle != null && !certKeyHandle.IsInvalid;

                if (hasCertificateAndKey)
                {
                    SetSslCertificate(innerContext, certHandle !, certKeyHandle !);
                }

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                {
                    Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate);
                }

                GCHandle alpnHandle = default;
                try
                {
                    if (sslAuthenticationOptions.ApplicationProtocols != null)
                    {
                        if (sslAuthenticationOptions.IsServer)
                        {
                            alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                            Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle));
                        }
                        else
                        {
                            if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0)
                            {
                                throw CreateSslException(SR.net_alpn_config_failed);
                            }
                        }
                    }

                    context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer);
                    Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create");
                    if (context.IsInvalid)
                    {
                        context.Dispose();
                        throw CreateSslException(SR.net_allocate_ssl_context_failed);
                    }

                    if (!sslAuthenticationOptions.IsServer)
                    {
                        // The IdnMapping converts unicode input into the IDNA punycode sequence.
                        string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !);

                        // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                        if (!Ssl.SslSetTlsExtHostName(context, punyCode))
                        {
                            Crypto.ErrClearError();
                        }
                    }

                    if (hasCertificateAndKey)
                    {
                        bool hasCertReference = false;
                        try
                        {
                            certHandle !.DangerousAddRef(ref hasCertReference);
                            using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle()))
                            {
                                X509Chain?chain = null;
                                try
                                {
                                    chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false);
                                    if (chain != null && !Ssl.AddExtraChainCertificates(context, chain))
                                    {
                                        throw CreateSslException(SR.net_ssl_use_cert_failed);
                                    }
                                }
                                finally
                                {
                                    if (chain != null)
                                    {
                                        int elementsCount = chain.ChainElements.Count;
                                        for (int i = 0; i < elementsCount; i++)
                                        {
                                            chain.ChainElements[i].Certificate !.Dispose();
                                        }

                                        chain.Dispose();
                                    }
                                }
                            }
                        }
                        finally
                        {
                            if (hasCertReference)
                            {
                                certHandle !.DangerousRelease();
                            }
                        }
                    }

                    context.AlpnHandle = alpnHandle;
                }
                catch
                {
                    if (alpnHandle.IsAllocated)
                    {
                        alpnHandle.Free();
                    }

                    throw;
                }
            }

            return(context);
        }
Exemplo n.º 7
0
        internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle context = null;

            // Always use SSLv23_method, regardless of protocols.  It supports negotiating to the highest
            // mutually supported version and can thus handle any of the set protocols, and we then use
            // SetProtocolOptions to ensure we only allow the ones requested.
            using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method))
            {
                if (innerContext.IsInvalid)
                {
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just
                // create the handle, it's rooted by the using, no one else has a reference to it, etc.
                Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols);

                // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet
                // shutdown (we aren't negotiating for session close to enable later session
                // restoration).
                //
                // If you find yourself wanting to remove this line to enable bidirectional
                // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect().
                // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
                Ssl.SslCtxSetQuietShutdown(innerContext);

                if (!Ssl.SetEncryptionPolicy(innerContext, policy))
                {
                    throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                }

                bool hasCertificateAndKey =
                    certHandle != null && !certHandle.IsInvalid &&
                    certKeyHandle != null && !certKeyHandle.IsInvalid;

                if (hasCertificateAndKey)
                {
                    SetSslCertificate(innerContext, certHandle, certKeyHandle);
                }

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                {
                    Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate);
                }

                GCHandle alpnHandle = default;
                try
                {
                    if (sslAuthenticationOptions.ApplicationProtocols != null)
                    {
                        if (sslAuthenticationOptions.IsServer)
                        {
                            alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                            Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle));
                        }
                        else
                        {
                            if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0)
                            {
                                throw CreateSslException(SR.net_alpn_config_failed);
                            }
                        }
                    }

                    context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer);
                    Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create");
                    if (context.IsInvalid)
                    {
                        context.Dispose();
                        throw CreateSslException(SR.net_allocate_ssl_context_failed);
                    }

                    if (!sslAuthenticationOptions.IsServer)
                    {
                        // The IdnMapping converts unicode input into the IDNA punycode sequence.
                        string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost);

                        // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                        Ssl.SslSetTlsExtHostName(context, punyCode);
                    }

                    if (hasCertificateAndKey)
                    {
                        bool hasCertReference = false;
                        try
                        {
                            certHandle.DangerousAddRef(ref hasCertReference);
                            using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle()))
                            {
                                using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false))
                                {
                                    if (chain != null && !Ssl.AddExtraChainCertificates(context, chain))
                                    {
                                        throw CreateSslException(SR.net_ssl_use_cert_failed);
                                    }
                                }
                            }
                        }
                        finally
                        {
                            if (hasCertReference)
                            {
                                certHandle.DangerousRelease();
                            }
                        }
                    }

                    context.AlpnHandle = alpnHandle;
                }
                catch
                {
                    if (alpnHandle.IsAllocated)
                    {
                        alpnHandle.Free();
                    }

                    throw;
                }
            }

            return(context);
        }
Exemplo n.º 8
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
            : base(credential)
        {
            Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                int osStatus;

                _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer);

                // Make sure the class instance is associated to the session and is provided
                // in the Read/Write callback connection parameter
                SslSetConnection(_sslContext);

                unsafe
                {
                    osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
                        _sslContext,
                        &ReadFromConnection,
                        &WriteToConnection);
                }

                if (osStatus != 0)
                {
                    throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                }

                if (sslAuthenticationOptions.CipherSuitesPolicy != null)
                {
                    uint[] tlsCipherSuites = sslAuthenticationOptions.CipherSuitesPolicy.Pal.TlsCipherSuites;

                    unsafe
                    {
                        fixed(uint *cipherSuites = tlsCipherSuites)
                        {
                            osStatus = Interop.AppleCrypto.SslSetEnabledCipherSuites(
                                _sslContext,
                                cipherSuites,
                                tlsCipherSuites.Length);

                            if (osStatus != 0)
                            {
                                throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                            }
                        }
                    }
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    // On OSX coretls supports only client side. For server, we will silently ignore the option.
                    if (!sslAuthenticationOptions.IsServer)
                    {
                        Interop.AppleCrypto.SslCtxSetAlpnProtos(_sslContext, sslAuthenticationOptions.ApplicationProtocols);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }
Exemplo n.º 9
0
        // This essentially wraps SSL* SSL_new()
        internal static SafeSslHandle AllocateSslHandle(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle?       sslHandle    = null;
            SafeSslContextHandle?sslCtxHandle = null;
            SafeSslContextHandle?newCtxHandle = null;
            SslProtocols         protocols    = CalculateEffectiveProtocols(sslAuthenticationOptions);
            bool cacheSslContext = !DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption &&
                                   sslAuthenticationOptions.CertificateContext != null &&
                                   sslAuthenticationOptions.CertificateContext.SslContexts != null &&
                                   sslAuthenticationOptions.CipherSuitesPolicy == null &&
                                   (!sslAuthenticationOptions.IsServer ||
                                    (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0));

            if (cacheSslContext)
            {
                sslAuthenticationOptions.CertificateContext !.SslContexts !.TryGetValue(protocols, out sslCtxHandle);
            }

            if (sslCtxHandle == null)
            {
                // We did not get SslContext from cache
                sslCtxHandle = newCtxHandle = AllocateSslContext(credential, sslAuthenticationOptions, protocols);

                if (cacheSslContext && sslAuthenticationOptions.CertificateContext !.SslContexts !.TryAdd(protocols, newCtxHandle))
                {
                    newCtxHandle = null;
                }
            }

            GCHandle alpnHandle = default;

            try
            {
                sslHandle = SafeSslHandle.Create(sslCtxHandle, sslAuthenticationOptions.IsServer);
                Debug.Assert(sslHandle != null, "Expected non-null return value from SafeSslHandle.Create");
                if (sslHandle.IsInvalid)
                {
                    sslHandle.Dispose();
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    if (sslAuthenticationOptions.IsServer)
                    {
                        alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                        Interop.Ssl.SslSetData(sslHandle, GCHandle.ToIntPtr(alpnHandle));
                        sslHandle.AlpnHandle = alpnHandle;
                    }
                    else
                    {
                        if (Interop.Ssl.SslSetAlpnProtos(sslHandle, sslAuthenticationOptions.ApplicationProtocols) != 0)
                        {
                            throw CreateSslException(SR.net_alpn_config_failed);
                        }
                    }
                }

                if (!sslAuthenticationOptions.IsServer)
                {
                    // The IdnMapping converts unicode input into the IDNA punycode sequence.
                    string punyCode = string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ? string.Empty : s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !);

                    // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                    if (!Ssl.SslSetTlsExtHostName(sslHandle, punyCode))
                    {
                        Crypto.ErrClearError();
                    }
                }

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                {
                    Ssl.SslSetVerifyPeer(sslHandle);
                }
            }
            catch
            {
                if (alpnHandle.IsAllocated)
                {
                    alpnHandle.Free();
                }

                throw;
            }
            finally
            {
                newCtxHandle?.Dispose();
            }

            return(sslHandle);
        }
Exemplo n.º 10
0
        // This essentially wraps SSL_CTX* aka SSL_CTX_new + setting
        internal static SafeSslContextHandle AllocateSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions, SslProtocols protocols)
        {
            SafeX509Handle?   certHandle    = credential.CertHandle;
            SafeEvpPKeyHandle?certKeyHandle = credential.CertKeyHandle;

            // Always use SSLv23_method, regardless of protocols.  It supports negotiating to the highest
            // mutually supported version and can thus handle any of the set protocols, and we then use
            // SetProtocolOptions to ensure we only allow the ones requested.
            SafeSslContextHandle sslCtx = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method);

            try
            {
                if (sslCtx.IsInvalid)
                {
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                Ssl.SslCtxSetProtocolOptions(sslCtx, protocols);

                if (sslAuthenticationOptions.EncryptionPolicy != EncryptionPolicy.RequireEncryption)
                {
                    // Sets policy and security level
                    if (!Ssl.SetEncryptionPolicy(sslCtx, sslAuthenticationOptions.EncryptionPolicy))
                    {
                        throw new SslException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                    }
                }

                byte[]? cipherList =
                    CipherSuitesPolicyPal.GetOpenSslCipherList(sslAuthenticationOptions.CipherSuitesPolicy, protocols, sslAuthenticationOptions.EncryptionPolicy);

                Debug.Assert(cipherList == null || (cipherList.Length >= 1 && cipherList[cipherList.Length - 1] == 0));

                byte[]? cipherSuites =
                    CipherSuitesPolicyPal.GetOpenSslCipherSuites(sslAuthenticationOptions.CipherSuitesPolicy, protocols, sslAuthenticationOptions.EncryptionPolicy);

                Debug.Assert(cipherSuites == null || (cipherSuites.Length >= 1 && cipherSuites[cipherSuites.Length - 1] == 0));

                unsafe
                {
                    fixed(byte *cipherListStr = cipherList)
                    fixed(byte *cipherSuitesStr = cipherSuites)
                    {
                        if (!Ssl.SslCtxSetCiphers(sslCtx, cipherListStr, cipherSuitesStr))
                        {
                            Crypto.ErrClearError();
                            throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                        }
                    }
                }

                // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet
                // shutdown (we aren't negotiating for session close to enable later session
                // restoration).
                //
                // If you find yourself wanting to remove this line to enable bidirectional
                // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect().
                // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
                Ssl.SslCtxSetQuietShutdown(sslCtx);

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    unsafe
                    {
                        Interop.Ssl.SslCtxSetAlpnSelectCb(sslCtx, &AlpnServerSelectCallback, IntPtr.Zero);
                    }
                }

                bool hasCertificateAndKey =
                    certHandle != null && !certHandle.IsInvalid &&
                    certKeyHandle != null && !certKeyHandle.IsInvalid;

                if (hasCertificateAndKey)
                {
                    SetSslCertificate(sslCtx, certHandle !, certKeyHandle !);
                }

                if (sslAuthenticationOptions.CertificateContext != null && sslAuthenticationOptions.CertificateContext.IntermediateCertificates.Length > 0)
                {
                    if (!Ssl.AddExtraChainCertificates(sslCtx, sslAuthenticationOptions.CertificateContext.IntermediateCertificates))
                    {
                        throw CreateSslException(SR.net_ssl_use_cert_failed);
                    }
                }
            }
            catch
            {
                sslCtx.Dispose();
                throw;
            }

            return(sslCtx);
        }
Exemplo n.º 11
0
        // This essentially wraps SSL* SSL_new()
        internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle?       sslHandle    = null;
            SafeSslContextHandle?sslCtxHandle = null;
            SafeSslContextHandle?newCtxHandle = null;
            SslProtocols         protocols    = CalculateEffectiveProtocols(sslAuthenticationOptions);
            bool hasAlpn         = sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0;
            bool cacheSslContext = !DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption && sslAuthenticationOptions.CipherSuitesPolicy == null;

            if (cacheSslContext)
            {
                if (sslAuthenticationOptions.IsClient)
                {
                    // We don't support client resume on old OpenSSL versions.
                    // We don't want to try on empty TargetName since that is our key.
                    // And we don't want to mess up with client authentication. It may be possible
                    // but it seems safe to get full new session.
                    if (!Interop.Ssl.Capabilities.Tls13Supported ||
                        string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ||
                        sslAuthenticationOptions.CertificateContext != null ||
                        sslAuthenticationOptions.CertSelectionDelegate != null)
                    {
                        cacheSslContext = false;
                    }
                }
                else
                {
                    // Server should always have certificate
                    Debug.Assert(sslAuthenticationOptions.CertificateContext != null);
                    if (sslAuthenticationOptions.CertificateContext == null ||
                        sslAuthenticationOptions.CertificateContext.SslContexts == null)
                    {
                        cacheSslContext = false;
                    }
                }
            }

            if (cacheSslContext)
            {
                if (sslAuthenticationOptions.IsServer)
                {
                    sslAuthenticationOptions.CertificateContext !.SslContexts !.TryGetValue(protocols | (hasAlpn ? FakeAlpnSslProtocol : SslProtocols.None), out sslCtxHandle);
                }
                else
                {
                    s_clientSslContexts.TryGetValue(protocols, out sslCtxHandle);
                }
            }

            if (sslCtxHandle == null)
            {
                // We did not get SslContext from cache
                sslCtxHandle = newCtxHandle = AllocateSslContext(sslAuthenticationOptions, protocols, cacheSslContext);

                if (cacheSslContext)
                {
                    bool added = sslAuthenticationOptions.IsServer ?
                                 sslAuthenticationOptions.CertificateContext !.SslContexts !.TryAdd(protocols | (SslProtocols)(hasAlpn ? 1 : 0), newCtxHandle) :
                                 s_clientSslContexts.TryAdd(protocols, newCtxHandle);
                    if (added)
                    {
                        newCtxHandle = null;
                    }
                }
            }

            GCHandle alpnHandle = default;

            try
            {
                sslHandle = SafeSslHandle.Create(sslCtxHandle, sslAuthenticationOptions.IsServer);
                Debug.Assert(sslHandle != null, "Expected non-null return value from SafeSslHandle.Create");
                if (sslHandle.IsInvalid)
                {
                    sslHandle.Dispose();
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    if (sslAuthenticationOptions.IsServer)
                    {
                        Debug.Assert(Interop.Ssl.SslGetData(sslHandle) == IntPtr.Zero);
                        alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                        Interop.Ssl.SslSetData(sslHandle, GCHandle.ToIntPtr(alpnHandle));
                        sslHandle.AlpnHandle = alpnHandle;
                    }
                    else
                    {
                        if (Interop.Ssl.SslSetAlpnProtos(sslHandle, sslAuthenticationOptions.ApplicationProtocols) != 0)
                        {
                            throw CreateSslException(SR.net_alpn_config_failed);
                        }
                    }
                }

                if (sslAuthenticationOptions.IsClient)
                {
                    // The IdnMapping converts unicode input into the IDNA punycode sequence.
                    string punyCode = string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ? string.Empty : s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !);

                    // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                    if (!Ssl.SslSetTlsExtHostName(sslHandle, punyCode))
                    {
                        Crypto.ErrClearError();
                    }

                    if (cacheSslContext && !string.IsNullOrEmpty(punyCode))
                    {
                        sslCtxHandle.TrySetSession(sslHandle, punyCode);
                        bool ignored = false;
                        sslCtxHandle.DangerousAddRef(ref ignored);
                        sslHandle.SslContextHandle = sslCtxHandle;
                    }

                    // relevant to TLS 1.3 only: if user supplied a client cert or cert callback,
                    // advertise that we are willing to send the certificate post-handshake.
                    if (sslAuthenticationOptions.ClientCertificates?.Count > 0 ||
                        sslAuthenticationOptions.CertSelectionDelegate != null)
                    {
                        Ssl.SslSetPostHandshakeAuth(sslHandle, 1);
                    }

                    // Set client cert callback, this will interrupt the handshake with SecurityStatusPalErrorCode.CredentialsNeeded
                    // if server actually requests a certificate.
                    Ssl.SslSetClientCertCallback(sslHandle, 1);
                }
                else // sslAuthenticationOptions.IsServer
                {
                    if (sslAuthenticationOptions.RemoteCertRequired)
                    {
                        Ssl.SslSetVerifyPeer(sslHandle);
                    }

                    if (sslAuthenticationOptions.CertificateContext != null)
                    {
                        if (sslAuthenticationOptions.CertificateContext.Trust?._sendTrustInHandshake == true)
                        {
                            SslCertificateTrust        trust    = sslAuthenticationOptions.CertificateContext !.Trust !;
                            X509Certificate2Collection certList = (trust._trustList ?? trust._store !.Certificates);

                            Debug.Assert(certList != null, "certList != null");
                            Span <IntPtr> handles = certList.Count <= 256 ?
                                                    stackalloc IntPtr[256] :
                                                    new IntPtr[certList.Count];

                            for (int i = 0; i < certList.Count; i++)
                            {
                                handles[i] = certList[i].Handle;
                            }

                            if (!Ssl.SslAddClientCAs(sslHandle, handles.Slice(0, certList.Count)))
                            {
                                // The method can fail only when the number of cert names exceeds the maximum capacity
                                // supported by STACK_OF(X509_NAME) structure, which should not happen under normal
                                // operation.
                                Debug.Fail("Failed to add issuer to trusted CA list.");
                            }
                        }

                        byte[]? ocspResponse = sslAuthenticationOptions.CertificateContext.GetOcspResponseNoWaiting();

                        if (ocspResponse != null)
                        {
                            Ssl.SslStapleOcsp(sslHandle, ocspResponse);
                        }
                    }
                }
            }
            catch
            {
                if (alpnHandle.IsAllocated)
                {
                    alpnHandle.Free();
                }

                throw;
            }
            finally
            {
                newCtxHandle?.Dispose();
            }

            return(sslHandle);
        }
Exemplo n.º 12
0
        // This essentially wraps SSL_CTX* aka SSL_CTX_new + setting
        internal static unsafe SafeSslContextHandle AllocateSslContext(SslAuthenticationOptions sslAuthenticationOptions, SslProtocols protocols, bool enableResume)
        {
            // Always use SSLv23_method, regardless of protocols.  It supports negotiating to the highest
            // mutually supported version and can thus handle any of the set protocols, and we then use
            // SetProtocolOptions to ensure we only allow the ones requested.
            SafeSslContextHandle sslCtx = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method);

            try
            {
                if (sslCtx.IsInvalid)
                {
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                Ssl.SslCtxSetProtocolOptions(sslCtx, protocols);

                if (sslAuthenticationOptions.EncryptionPolicy != EncryptionPolicy.RequireEncryption)
                {
                    // Sets policy and security level
                    if (!Ssl.SetEncryptionPolicy(sslCtx, sslAuthenticationOptions.EncryptionPolicy))
                    {
                        throw new SslException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                    }
                }

                ReadOnlySpan <byte> cipherList = CipherSuitesPolicyPal.GetOpenSslCipherList(sslAuthenticationOptions.CipherSuitesPolicy, protocols, sslAuthenticationOptions.EncryptionPolicy);
                Debug.Assert(cipherList.IsEmpty || cipherList[^ 1] == 0);

                byte[]? cipherSuites = CipherSuitesPolicyPal.GetOpenSslCipherSuites(sslAuthenticationOptions.CipherSuitesPolicy, protocols, sslAuthenticationOptions.EncryptionPolicy);
                Debug.Assert(cipherSuites == null || (cipherSuites.Length >= 1 && cipherSuites[cipherSuites.Length - 1] == 0));

                fixed(byte *cipherListStr = cipherList)
                fixed(byte *cipherSuitesStr = cipherSuites)
                {
                    if (!Ssl.SslCtxSetCiphers(sslCtx, cipherListStr, cipherSuitesStr))
                    {
                        Crypto.ErrClearError();
                        throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                    }
                }

                // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet
                // shutdown (we aren't negotiating for session close to enable later session
                // restoration).
                //
                // If you find yourself wanting to remove this line to enable bidirectional
                // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect().
                // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
                Ssl.SslCtxSetQuietShutdown(sslCtx);

                if (enableResume)
                {
                    if (sslAuthenticationOptions.IsServer)
                    {
                        Ssl.SslCtxSetCaching(sslCtx, 1, s_cacheSize, null, null);
                    }
                    else
                    {
                        int result = Ssl.SslCtxSetCaching(sslCtx, 1, s_cacheSize, &NewSessionCallback, &RemoveSessionCallback);
                        Debug.Assert(result == 1);
                        sslCtx.EnableSessionCache();
                    }
                }
                else
                {
                    Ssl.SslCtxSetCaching(sslCtx, 0, -1, null, null);
                }

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    Interop.Ssl.SslCtxSetAlpnSelectCb(sslCtx, &AlpnServerSelectCallback, IntPtr.Zero);
                }

                if (sslAuthenticationOptions.CertificateContext != null)
                {
                    SetSslCertificate(sslCtx, sslAuthenticationOptions.CertificateContext.CertificateHandle, sslAuthenticationOptions.CertificateContext.KeyHandle);

                    if (sslAuthenticationOptions.CertificateContext.IntermediateCertificates.Length > 0)
                    {
                        if (!Ssl.AddExtraChainCertificates(sslCtx, sslAuthenticationOptions.CertificateContext.IntermediateCertificates))
                        {
                            throw CreateSslException(SR.net_ssl_use_cert_failed);
                        }
                    }

                    if (sslAuthenticationOptions.CertificateContext.OcspStaplingAvailable)
                    {
                        Ssl.SslCtxSetDefaultOcspCallback(sslCtx);
                    }
                }
            }
            catch
            {
                sslCtx.Dispose();
                throw;
            }

            return(sslCtx);
        }
Exemplo n.º 13
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
            : base(credential)
        {
            Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                int osStatus;

                _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer);

                // Make sure the class instance is associated to the session and is provided
                // in the Read/Write callback connection parameter
                SslSetConnection(_sslContext);

                unsafe
                {
                    osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
                        _sslContext,
                        &ReadFromConnection,
                        &WriteToConnection);
                }

                if (osStatus != 0)
                {
                    throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                }

                if (sslAuthenticationOptions.CipherSuitesPolicy != null)
                {
                    uint[] tlsCipherSuites = sslAuthenticationOptions.CipherSuitesPolicy.Pal.TlsCipherSuites;

                    unsafe
                    {
                        fixed(uint *cipherSuites = tlsCipherSuites)
                        {
                            osStatus = Interop.AppleCrypto.SslSetEnabledCipherSuites(
                                _sslContext,
                                cipherSuites,
                                tlsCipherSuites.Length);

                            if (osStatus != 0)
                            {
                                throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                            }
                        }
                    }
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    // On OSX coretls supports only client side. For server, we will silently ignore the option.
                    if (!sslAuthenticationOptions.IsServer)
                    {
                        Interop.AppleCrypto.SslCtxSetAlpnProtos(_sslContext, sslAuthenticationOptions.ApplicationProtocols);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }

            if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer)
            {
                Interop.AppleCrypto.SslSetTargetName(_sslContext, sslAuthenticationOptions.TargetHost);
            }

            if (sslAuthenticationOptions.CertificateContext == null && sslAuthenticationOptions.CertSelectionDelegate != null)
            {
                // certificate was not provided but there is user callback. We can break handshake if server asks for certificate
                // and we can try to get it based on remote certificate and trusted issuers.
                Interop.AppleCrypto.SslBreakOnCertRequested(_sslContext, true);
            }

            if (sslAuthenticationOptions.IsServer)
            {
                if (sslAuthenticationOptions.RemoteCertRequired)
                {
                    Interop.AppleCrypto.SslSetAcceptClientCert(_sslContext);
                }

                if (sslAuthenticationOptions.CertificateContext?.Trust?._sendTrustInHandshake == true)
                {
                    SslCertificateTrust        trust    = sslAuthenticationOptions.CertificateContext !.Trust !;
                    X509Certificate2Collection certList = (trust._trustList ?? trust._store !.Certificates);

                    Debug.Assert(certList != null, "certList != null");
                    Span <IntPtr> handles = certList.Count <= 256
                        ? stackalloc IntPtr[256]
                        : new IntPtr[certList.Count];

                    for (int i = 0; i < certList.Count; i++)
                    {
                        handles[i] = certList[i].Handle;
                    }

                    Interop.AppleCrypto.SslSetCertificateAuthorities(_sslContext, handles.Slice(0, certList.Count), true);
                }
            }
        }
Exemplo n.º 14
0
        // This essentially wraps SSL* SSL_new()
        internal static SafeSslHandle AllocateSslHandle(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle?       sslHandle    = null;
            SafeSslContextHandle?sslCtxHandle = null;
            SafeSslContextHandle?newCtxHandle = null;
            SslProtocols         protocols    = CalculateEffectiveProtocols(sslAuthenticationOptions);
            bool hasAlpn         = sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0;
            bool cacheSslContext = !DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption &&
                                   sslAuthenticationOptions.IsServer &&
                                   sslAuthenticationOptions.CertificateContext != null &&
                                   sslAuthenticationOptions.CertificateContext.SslContexts != null &&
                                   sslAuthenticationOptions.CipherSuitesPolicy == null;

            if (cacheSslContext)
            {
                sslAuthenticationOptions.CertificateContext !.SslContexts !.TryGetValue(protocols | (SslProtocols)(hasAlpn ? 1 : 0), out sslCtxHandle);
            }

            if (sslCtxHandle == null)
            {
                // We did not get SslContext from cache
                sslCtxHandle = newCtxHandle = AllocateSslContext(credential, sslAuthenticationOptions, protocols, cacheSslContext);

                if (cacheSslContext && sslAuthenticationOptions.CertificateContext !.SslContexts !.TryAdd(protocols | (SslProtocols)(hasAlpn ? 1 : 0), newCtxHandle))
                {
                    newCtxHandle = null;
                }
            }

            GCHandle alpnHandle = default;

            try
            {
                sslHandle = SafeSslHandle.Create(sslCtxHandle, sslAuthenticationOptions.IsServer);
                Debug.Assert(sslHandle != null, "Expected non-null return value from SafeSslHandle.Create");
                if (sslHandle.IsInvalid)
                {
                    sslHandle.Dispose();
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    if (sslAuthenticationOptions.IsServer)
                    {
                        alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                        Interop.Ssl.SslSetData(sslHandle, GCHandle.ToIntPtr(alpnHandle));
                        sslHandle.AlpnHandle = alpnHandle;
                    }
                    else
                    {
                        if (Interop.Ssl.SslSetAlpnProtos(sslHandle, sslAuthenticationOptions.ApplicationProtocols) != 0)
                        {
                            throw CreateSslException(SR.net_alpn_config_failed);
                        }
                    }
                }

                if (!sslAuthenticationOptions.IsServer)
                {
                    // The IdnMapping converts unicode input into the IDNA punycode sequence.
                    string punyCode = string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ? string.Empty : s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !);

                    // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                    if (!Ssl.SslSetTlsExtHostName(sslHandle, punyCode))
                    {
                        Crypto.ErrClearError();
                    }

                    // relevant to TLS 1.3 only: if user supplied a client cert or cert callback,
                    // advertise that we are willing to send the certificate post-handshake.
                    if (sslAuthenticationOptions.ClientCertificates?.Count > 0 ||
                        sslAuthenticationOptions.CertSelectionDelegate != null)
                    {
                        Ssl.SslSetPostHandshakeAuth(sslHandle, 1);
                    }

                    // Set client cert callback, this will interrupt the handshake with SecurityStatusPalErrorCode.CredentialsNeeded
                    // if server actually requests a certificate.
                    Ssl.SslSetClientCertCallback(sslHandle, 1);
                }

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                {
                    Ssl.SslSetVerifyPeer(sslHandle);
                }
            }
            catch
            {
                if (alpnHandle.IsAllocated)
                {
                    alpnHandle.Free();
                }

                throw;
            }
            finally
            {
                newCtxHandle?.Dispose();
            }

            return(sslHandle);
        }