You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
5.3 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright © 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.Collections.Generic;
  25. using System.IO;
  26. using System.Text;
  27. using System.Security.Cryptography;
  28. #if NETSTANDARD1_3
  29. using AliasText = Externals.MySql.Data.MySqlClient.Framework.NetStandard1_3;
  30. #else
  31. using AliasText = System.Text;
  32. #endif
  33. namespace Externals.MySql.Data.MySqlClient.Authentication
  34. {
  35. /// <summary>
  36. /// The implementation of the sha256_password authentication plugin.
  37. /// </summary>
  38. internal class Sha256AuthenticationPlugin : MySqlAuthenticationPlugin
  39. {
  40. /// <summary>
  41. /// The byte array representation of the public key provided by the server.
  42. /// </summary>
  43. protected byte[] rawPubkey;
  44. public override string PluginName => "sha256_password";
  45. protected override byte[] MoreData(byte[] data)
  46. {
  47. rawPubkey = data;
  48. byte[] buffer = GetNonLengthEncodedPassword();
  49. return buffer;
  50. }
  51. public override object GetPassword()
  52. {
  53. if (Settings.SslMode != MySqlSslMode.None)
  54. {
  55. byte[] passBytes = Encoding.GetBytes(Settings.Password);
  56. byte[] buffer = new byte[passBytes.Length + 2];
  57. Array.Copy(passBytes, 0, buffer, 1, passBytes.Length);
  58. buffer[0] = (byte)(passBytes.Length + 1);
  59. buffer[buffer.Length - 1] = 0x00;
  60. return buffer;
  61. }
  62. else
  63. {
  64. if (Settings.Password.Length == 0) return new byte[1];
  65. // Send RSA encrypted, since the channel is not protected.
  66. else if (rawPubkey == null) return new byte[] { 0x01 };
  67. else if (!Settings.AllowPublicKeyRetrieval)
  68. throw new MySqlException(Resources.RSAPublicKeyRetrievalNotEnabled);
  69. else
  70. {
  71. byte[] bytes = GetRsaPassword(Settings.Password, AuthenticationData, rawPubkey);
  72. if (bytes != null && bytes.Length == 1 && bytes[0] == 0) return null;
  73. return bytes;
  74. }
  75. }
  76. }
  77. private byte[] GetNonLengthEncodedPassword()
  78. {
  79. // Required for AuthChange requests.
  80. if (Settings.SslMode != MySqlSslMode.None)
  81. {
  82. // Send as clear text, since the channel is already encrypted.
  83. byte[] passBytes = Encoding.GetBytes(Settings.Password);
  84. byte[] buffer = new byte[passBytes.Length + 1];
  85. Array.Copy(passBytes, 0, buffer, 0, passBytes.Length);
  86. buffer[passBytes.Length] = 0;
  87. return buffer;
  88. }
  89. else return GetPassword() as byte[];
  90. }
  91. private byte[] GetRsaPassword(string password, byte[] seedBytes, byte[] rawPublicKey)
  92. {
  93. if (password.Length == 0) return new byte[1];
  94. // Obfuscate the plain text password with the session scramble.
  95. byte[] obfuscated = GetXor(AliasText.Encoding.Default.GetBytes(password), seedBytes);
  96. // Encrypt the password and send it to the server.
  97. #if NETSTANDARD1_3
  98. RSA rsa = MySqlPemReader.ConvertPemToRSAProvider(rawPublicKey);
  99. if (rsa == null)
  100. throw new MySqlException(Resources.UnableToReadRSAKey);
  101. return rsa.Encrypt(obfuscated, RSAEncryptionPadding.OaepSHA1);
  102. #else
  103. RSACryptoServiceProvider rsa = MySqlPemReader.ConvertPemToRSAProvider(rawPublicKey);
  104. if (rsa == null)
  105. throw new MySqlException(Resources.UnableToReadRSAKey);
  106. return rsa.Encrypt(obfuscated, true);
  107. #endif
  108. }
  109. /// <summary>
  110. /// Applies XOR to the byte arrays provided as input.
  111. /// </summary>
  112. /// <returns>A byte array that contains the results of the XOR operation.</returns>
  113. protected byte[] GetXor(byte[] src, byte[] pattern)
  114. {
  115. byte[] src2 = new byte[src.Length + 1];
  116. Array.Copy(src, 0, src2, 0, src.Length);
  117. src2[src.Length] = 0;
  118. byte[] result = new byte[src2.Length];
  119. for (int i = 0; i < src2.Length; i++)
  120. {
  121. result[i] = (byte)(src2[i] ^ (pattern[i % pattern.Length]));
  122. }
  123. return result;
  124. }
  125. }
  126. }
  127. #endif