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; } }
// 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); }
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; } }
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); } } }
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; } }
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); }
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); }
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; } }
// 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); }
// 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); }
// 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); }
// 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); }
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); } } }
// 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); }