public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, bool useCompression, CharacterSet characterSet, byte[] connectionAttributes) { // TODO: verify server capabilities var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, useCompression, characterSet); writer.WriteNullTerminatedString(cs.UserID); var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, 0, cs.Password); writer.Write((byte)authenticationResponse.Length); writer.Write(authenticationResponse); if (!string.IsNullOrWhiteSpace(cs.Database)) { writer.WriteNullTerminatedString(cs.Database); } if ((handshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0) { writer.WriteNullTerminatedString("mysql_native_password"); } if (connectionAttributes != null) { writer.Write(connectionAttributes); } return(writer.ToPayloadData()); }
public async Task <bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { VerifyState(State.Connected); try { if (ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await SendAsync(ResetConnectionPayload.Create(), ioBehavior, cancellationToken).ConfigureAwait(false); var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); // the "reset connection" packet also resets the connection charset, so we need to change that back to our default payload = QueryPayload.Create("SET NAMES utf8mb4 COLLATE utf8mb4_bin;"); await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } else { // optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL) var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password); var payload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_supportsConnectionAttributes ? s_connectionAttributes : null); await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) { payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false); } OkPayload.Create(payload); } return(true); } catch (IOException) { } catch (SocketException) { } return(false); }
public static byte[] Create(InitialHandshakePacket handshake, ConnectionSettings cs) { // TODO: verify server capabilities var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs); writer.WriteNullTerminatedString(cs.UserID); var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, 0, cs.Password); writer.WriteByte((byte)authenticationResponse.Length); writer.Write(authenticationResponse); if (!string.IsNullOrWhiteSpace(cs.Database)) { writer.WriteNullTerminatedString(cs.Database); } if ((handshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0) { writer.WriteNullTerminatedString("mysql_native_password"); } return(writer.ToBytes()); }
private async Task <PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs, PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken) { // if the server didn't support the hashed password; rehash with the new challenge var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); switch (switchRequest.Name) { case "mysql_native_password": AuthPluginData = switchRequest.Data; var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password); payload = new PayloadData(hashedPassword); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false)); case "mysql_clear_password": if (!m_isSecureConnection) { throw new MySqlException("Authentication method '{0}' requires a secure connection.".FormatInvariant(switchRequest.Name)); } payload = new PayloadData(Encoding.UTF8.GetBytes(cs.Password)); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false)); case "caching_sha2_password": var scrambleBytes = AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(switchRequest.Data), cs.Password); payload = new PayloadData(scrambleBytes); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload); if (cachingSha2ServerResponsePayload.Succeeded) { return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false)); } goto case "sha256_password"; case "sha256_password": if (!m_isSecureConnection && cs.Password.Length > 1) { #if NET45 throw new MySqlException("Authentication method '{0}' requires a secure connection (prior to .NET 4.6).".FormatInvariant(switchRequest.Name)); #else var publicKey = await GetRsaPublicKeyAsync(switchRequest.Name, cs, ioBehavior, cancellationToken).ConfigureAwait(false); return(await SendEncryptedPasswordAsync(switchRequest, publicKey, cs, ioBehavior, cancellationToken).ConfigureAwait(false)); #endif } else { return(await SendClearPasswordAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false)); } case "mysql_old_password": throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html."); default: throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(switchRequest.Name)); } }