public async Task ConnectAsync(IEnumerable <string> hosts, int port, string userId, string password, string database, IOBehavior ioBehavior, CancellationToken cancellationToken) { var connected = await OpenSocketAsync(hosts, port, ioBehavior, cancellationToken).ConfigureAwait(false); if (!connected) { throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") { throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName)); } ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); ConnectionId = initialHandshake.ConnectionId; AuthPluginData = initialHandshake.AuthPluginData; var response = HandshakeResponse41Packet.Create(initialHandshake, userId, password, database); payload = new PayloadData(new ArraySegment <byte>(response)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); }
public async Task ConnectAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { var connected = false; if (cs.ConnectionType == ConnectionType.Tcp) { connected = await OpenTcpSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } else if (cs.ConnectionType == ConnectionType.Unix) { connected = await OpenUnixSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } if (!connected) { throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var socketByteHandler = new SocketByteHandler(m_socket); m_payloadHandler = new StandardPayloadHandler(socketByteHandler); var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") { throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName)); } ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); ConnectionId = initialHandshake.ConnectionId; AuthPluginData = initialHandshake.AuthPluginData; if (cs.UseCompression && !initialHandshake.ProtocolCapabilities.HasFlag(ProtocolCapabilities.Compress)) { cs = cs.WithUseCompression(false); } if (cs.SslMode != MySqlSslMode.None) { await InitSslAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } var response = HandshakeResponse41Packet.Create(initialHandshake, cs); payload = new PayloadData(new ArraySegment <byte>(response)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); if (cs.UseCompression) { m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler); } }
private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { X509CertificateCollection clientCertificates = null; if (cs.CertificateFile != null) { try { var certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword); #if !NET45 m_clientCertificate = certificate; #endif clientCertificates = new X509CertificateCollection { certificate }; } catch (CryptographicException ex) { if (!File.Exists(cs.CertificateFile)) { throw new MySqlException("Cannot find Certificate File", ex); } throw new MySqlException("Either the Certificate Password is incorrect or the Certificate File is invalid", ex); } } X509Chain caCertificateChain = null; if (cs.CACertificateFile != null) { try { var caCertificate = new X509Certificate2(cs.CACertificateFile); #if !NET45 m_serverCertificate = caCertificate; #endif caCertificateChain = new X509Chain { ChainPolicy = { RevocationMode = X509RevocationMode.NoCheck, VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority } }; caCertificateChain.ChainPolicy.ExtraStore.Add(caCertificate); } catch (CryptographicException ex) { if (!File.Exists(cs.CACertificateFile)) { throw new MySqlException("Cannot find CA Certificate File", ex); } throw new MySqlException("The CA Certificate File is invalid", ex); } } X509Certificate ValidateLocalCertificate(object lcbSender, string lcbTargetHost, X509CertificateCollection lcbLocalCertificates, X509Certificate lcbRemoteCertificate, string[] lcbAcceptableIssuers) => lcbLocalCertificates[0]; bool ValidateRemoteCertificate(object rcbSender, X509Certificate rcbCertificate, X509Chain rcbChain, SslPolicyErrors rcbPolicyErrors) { if (cs.SslMode == MySqlSslMode.Preferred || cs.SslMode == MySqlSslMode.Required) { return(true); } if ((rcbPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 && caCertificateChain != null) { if (caCertificateChain.Build((X509Certificate2)rcbCertificate)) { var chainStatus = caCertificateChain.ChainStatus[0].Status & ~X509ChainStatusFlags.UntrustedRoot; if (chainStatus == X509ChainStatusFlags.NoError) { rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors; } } } if (cs.SslMode == MySqlSslMode.VerifyCA) { rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch; } return(rcbPolicyErrors == SslPolicyErrors.None); } SslStream sslStream; if (clientCertificates == null) { sslStream = new SslStream(m_networkStream, false, ValidateRemoteCertificate); } else { sslStream = new SslStream(m_networkStream, false, ValidateRemoteCertificate, ValidateLocalCertificate); } // SslProtocols.Tls1.2 throws an exception in Windows, see https://github.com/mysql-net/MySqlConnector/pull/101 var sslProtocols = SslProtocols.Tls | SslProtocols.Tls11; if (!Utility.IsWindows()) { sslProtocols |= SslProtocols.Tls12; } var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull; var initSsl = new PayloadData(new ArraySegment <byte>(HandshakeResponse41Packet.InitSsl(serverCapabilities, cs, m_useCompression))); await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false); try { if (ioBehavior == IOBehavior.Asynchronous) { await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); } else { #if NETSTANDARD1_3 await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); #else sslStream.AuthenticateAsClient(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation); #endif } var sslByteHandler = new StreamByteHandler(sslStream); m_payloadHandler.ByteHandler = sslByteHandler; m_isSecureConnection = true; } catch (Exception ex) { sslStream.Dispose(); ShutdownSocket(); m_hostname = ""; lock (m_lock) m_state = State.Failed; if (ex is AuthenticationException) { throw new MySqlException("SSL Authentication Error", ex); } if (ex is IOException && clientCertificates != null) { throw new MySqlException("MySQL Server rejected client certificate", ex); } throw; } }
public async Task ConnectAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { lock (m_lock) { VerifyState(State.Created); m_state = State.Connecting; } var connected = false; if (cs.ConnectionType == ConnectionType.Tcp) { connected = await OpenTcpSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } else if (cs.ConnectionType == ConnectionType.Unix) { connected = await OpenUnixSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } if (!connected) { lock (m_lock) m_state = State.Failed; throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var byteHandler = new SocketByteHandler(m_socket); m_payloadHandler = new StandardPayloadHandler(byteHandler); var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); // if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use string authPluginName; if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0) { authPluginName = initialHandshake.AuthPluginName; } else { authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password"; } if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password") { throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName)); } ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); ConnectionId = initialHandshake.ConnectionId; AuthPluginData = initialHandshake.AuthPluginData; m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0; var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0; if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl)) { if (!serverSupportsSsl) { throw new MySqlException("Server does not support SSL"); } await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, ioBehavior, cancellationToken).ConfigureAwait(false); } m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0; if (m_supportsConnectionAttributes && s_connectionAttributes == null) { s_connectionAttributes = CreateConnectionAttributes(); } m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0; var response = HandshakeResponse41Packet.Create(initialHandshake, cs, m_useCompression, m_supportsConnectionAttributes ? s_connectionAttributes : null); payload = new PayloadData(new ArraySegment <byte>(response)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); // if server doesn't support the authentication fast path, it will send a new challenge if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) { payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false); } OkPayload.Create(payload); if (m_useCompression) { m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler); } }
private async Task InitSslAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { X509Certificate2 certificate; try { certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword); } catch (CryptographicException ex) { if (!File.Exists(cs.CertificateFile)) { throw new MySqlException("Cannot find SSL Certificate File", ex); } throw new MySqlException("Either the SSL Certificate Password is incorrect or the SSL Certificate File is invalid", ex); } Func <object, string, X509CertificateCollection, X509Certificate, string[], X509Certificate> localCertificateCb = (lcbSender, lcbTargetHost, lcbLocalCertificates, lcbRemoteCertificate, lcbAcceptableIssuers) => lcbLocalCertificates[0]; Func <object, X509Certificate, X509Chain, SslPolicyErrors, bool> remoteCertificateCb = (rcbSender, rcbCertificate, rcbChain, rcbPolicyErrors) => { switch (rcbPolicyErrors) { case SslPolicyErrors.None: return(true); case SslPolicyErrors.RemoteCertificateNameMismatch: return(cs.SslMode != MySqlSslMode.VerifyFull); default: return(cs.SslMode == MySqlSslMode.Required); } }; var sslStream = new SslStream(m_networkStream, false, new RemoteCertificateValidationCallback(remoteCertificateCb), new LocalCertificateSelectionCallback(localCertificateCb)); var clientCertificates = new X509CertificateCollection { certificate }; // SslProtocols.Tls1.2 throws an exception in Windows, see https://github.com/mysql-net/MySqlConnector/pull/101 var sslProtocols = SslProtocols.Tls | SslProtocols.Tls11; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { sslProtocols |= SslProtocols.Tls12; } var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull; var initSsl = new PayloadData(new ArraySegment <byte>(HandshakeResponse41Packet.InitSsl(cs))); await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false); try { if (ioBehavior == IOBehavior.Asynchronous) { await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); } else { #if NETSTANDARD1_3 await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); #else sslStream.AuthenticateAsClient(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation); #endif } var sslByteHandler = new StreamByteHandler(sslStream); m_payloadHandler.ByteHandler = sslByteHandler; } catch (AuthenticationException ex) { ShutdownSocket(); m_hostname = ""; m_state = State.Failed; throw new MySqlException("SSL Authentication Error", ex); } }
public async Task ConnectAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { var connected = false; if (cs.ConnectionType == ConnectionType.Tcp) { connected = await OpenTcpSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } else if (cs.ConnectionType == ConnectionType.Unix) { connected = await OpenUnixSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } if (!connected) { throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var socketByteHandler = new SocketByteHandler(m_socket); m_payloadHandler = new StandardPayloadHandler(socketByteHandler); var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); // if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use string authPluginName; if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0) { authPluginName = initialHandshake.AuthPluginName; } else { authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password"; } if (authPluginName != "mysql_native_password") { throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName)); } ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); ConnectionId = initialHandshake.ConnectionId; AuthPluginData = initialHandshake.AuthPluginData; if (cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) == 0) { cs = cs.WithUseCompression(false); } var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0; if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl)) { if (!serverSupportsSsl) { throw new MySqlException("Server does not support SSL"); } await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, ioBehavior, cancellationToken).ConfigureAwait(false); } var response = HandshakeResponse41Packet.Create(initialHandshake, cs); payload = new PayloadData(new ArraySegment <byte>(response)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); if (cs.UseCompression) { m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler); } }