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.

473 lines
16 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright ?2012, 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.Diagnostics;
  25. using System.Runtime.InteropServices;
  26. using System.Security;
  27. using Externals.MySql.Data.MySqlClient;
  28. namespace Externals.MySql.Data.MySqlClient.Authentication
  29. {
  30. /// <summary>
  31. ///
  32. /// </summary>
  33. #if !NETSTANDARD1_3
  34. [SuppressUnmanagedCodeSecurity()]
  35. #endif
  36. internal class MySqlWindowsAuthenticationPlugin : MySqlAuthenticationPlugin
  37. {
  38. SECURITY_HANDLE outboundCredentials = new SECURITY_HANDLE(0);
  39. SECURITY_HANDLE clientContext = new SECURITY_HANDLE(0);
  40. SECURITY_INTEGER lifetime = new SECURITY_INTEGER(0);
  41. bool continueProcessing;
  42. string targetName = null;
  43. protected override void CheckConstraints()
  44. {
  45. string platform = String.Empty;
  46. #if !NETSTANDARD1_3
  47. int p = (int)Environment.OSVersion.Platform;
  48. if ((p == 4) || (p == 128))
  49. platform = "Unix";
  50. else if (Environment.OSVersion.Platform == PlatformID.MacOSX)
  51. platform = "Mac OS/X";
  52. if (!String.IsNullOrEmpty(platform))
  53. throw new MySqlException(String.Format(Resources.WinAuthNotSupportOnPlatform, platform));
  54. #endif
  55. base.CheckConstraints();
  56. }
  57. public override string GetUsername()
  58. {
  59. string username = base.GetUsername();
  60. if (String.IsNullOrEmpty(username))
  61. return "auth_windows";
  62. return username;
  63. }
  64. public override string PluginName
  65. {
  66. get { return "authentication_windows_client"; }
  67. }
  68. protected override byte[] MoreData(byte[] moreData)
  69. {
  70. if (moreData == null)
  71. AcquireCredentials();
  72. byte[] clientBlob = null;
  73. if (continueProcessing)
  74. InitializeClient(out clientBlob, moreData, out continueProcessing);
  75. if (!continueProcessing || clientBlob == null || clientBlob.Length == 0)
  76. {
  77. FreeCredentialsHandle(ref outboundCredentials);
  78. DeleteSecurityContext(ref clientContext);
  79. return null;
  80. }
  81. return clientBlob;
  82. }
  83. void InitializeClient(out byte[] clientBlob, byte[] serverBlob, out bool continueProcessing)
  84. {
  85. clientBlob = null;
  86. continueProcessing = true;
  87. SecBufferDesc clientBufferDesc = new SecBufferDesc(MAX_TOKEN_SIZE);
  88. SECURITY_INTEGER initLifetime = new SECURITY_INTEGER(0);
  89. int ss = -1;
  90. try
  91. {
  92. uint ContextAttributes = 0;
  93. if (serverBlob == null)
  94. {
  95. ss = InitializeSecurityContext(
  96. ref outboundCredentials,
  97. IntPtr.Zero,
  98. targetName,
  99. STANDARD_CONTEXT_ATTRIBUTES,
  100. 0,
  101. SECURITY_NETWORK_DREP,
  102. IntPtr.Zero, /* always zero first time around */
  103. 0,
  104. out clientContext,
  105. out clientBufferDesc,
  106. out ContextAttributes,
  107. out initLifetime);
  108. }
  109. else
  110. {
  111. SecBufferDesc serverBufferDesc = new SecBufferDesc(serverBlob);
  112. try
  113. {
  114. ss = InitializeSecurityContext(ref outboundCredentials,
  115. ref clientContext,
  116. targetName,
  117. STANDARD_CONTEXT_ATTRIBUTES,
  118. 0,
  119. SECURITY_NETWORK_DREP,
  120. ref serverBufferDesc,
  121. 0,
  122. out clientContext,
  123. out clientBufferDesc,
  124. out ContextAttributes,
  125. out initLifetime);
  126. }
  127. finally
  128. {
  129. serverBufferDesc.Dispose();
  130. }
  131. }
  132. if ((SEC_I_COMPLETE_NEEDED == ss)
  133. || (SEC_I_COMPLETE_AND_CONTINUE == ss))
  134. {
  135. CompleteAuthToken(ref clientContext, ref clientBufferDesc);
  136. }
  137. if (ss != SEC_E_OK &&
  138. ss != SEC_I_CONTINUE_NEEDED &&
  139. ss != SEC_I_COMPLETE_NEEDED &&
  140. ss != SEC_I_COMPLETE_AND_CONTINUE)
  141. {
  142. throw new MySqlException(
  143. "InitializeSecurityContext() failed with errorcode " + ss);
  144. }
  145. clientBlob = clientBufferDesc.GetSecBufferByteArray();
  146. }
  147. finally
  148. {
  149. clientBufferDesc.Dispose();
  150. }
  151. continueProcessing = (ss != SEC_E_OK && ss != SEC_I_COMPLETE_NEEDED);
  152. }
  153. private void AcquireCredentials()
  154. {
  155. continueProcessing = true;
  156. int ss = AcquireCredentialsHandle(null, "Negotiate", SECPKG_CRED_OUTBOUND,
  157. IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, ref outboundCredentials,
  158. ref lifetime);
  159. if (ss != SEC_E_OK)
  160. throw new MySqlException("AcquireCredentialsHandle failed with errorcode" + ss);
  161. }
  162. #region SSPI Constants and Imports
  163. const int SEC_E_OK = 0;
  164. const int SEC_I_CONTINUE_NEEDED = 0x90312;
  165. const int SEC_I_COMPLETE_NEEDED = 0x1013;
  166. const int SEC_I_COMPLETE_AND_CONTINUE = 0x1014;
  167. const int SECPKG_CRED_OUTBOUND = 2;
  168. const int SECURITY_NETWORK_DREP = 0;
  169. const int SECURITY_NATIVE_DREP = 0x10;
  170. const int SECPKG_CRED_INBOUND = 1;
  171. const int MAX_TOKEN_SIZE = 12288;
  172. const int SECPKG_ATTR_SIZES = 0;
  173. const int STANDARD_CONTEXT_ATTRIBUTES = 0;
  174. [DllImport("secur32", CharSet = CharSet.Unicode)]
  175. static extern int AcquireCredentialsHandle(
  176. string pszPrincipal,
  177. string pszPackage,
  178. int fCredentialUse,
  179. IntPtr PAuthenticationID,
  180. IntPtr pAuthData,
  181. int pGetKeyFn,
  182. IntPtr pvGetKeyArgument,
  183. ref SECURITY_HANDLE phCredential,
  184. ref SECURITY_INTEGER ptsExpiry);
  185. [DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
  186. static extern int InitializeSecurityContext(
  187. ref SECURITY_HANDLE phCredential,
  188. IntPtr phContext,
  189. string pszTargetName,
  190. int fContextReq,
  191. int Reserved1,
  192. int TargetDataRep,
  193. IntPtr pInput,
  194. int Reserved2,
  195. out SECURITY_HANDLE phNewContext,
  196. out SecBufferDesc pOutput,
  197. out uint pfContextAttr,
  198. out SECURITY_INTEGER ptsExpiry);
  199. [DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
  200. static extern int InitializeSecurityContext(
  201. ref SECURITY_HANDLE phCredential,
  202. ref SECURITY_HANDLE phContext,
  203. string pszTargetName,
  204. int fContextReq,
  205. int Reserved1,
  206. int TargetDataRep,
  207. ref SecBufferDesc SecBufferDesc,
  208. int Reserved2,
  209. out SECURITY_HANDLE phNewContext,
  210. out SecBufferDesc pOutput,
  211. out uint pfContextAttr,
  212. out SECURITY_INTEGER ptsExpiry);
  213. [DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
  214. static extern int CompleteAuthToken(
  215. ref SECURITY_HANDLE phContext,
  216. ref SecBufferDesc pToken);
  217. [DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
  218. public static extern int QueryContextAttributes(
  219. ref SECURITY_HANDLE phContext,
  220. uint ulAttribute,
  221. out SecPkgContext_Sizes pContextAttributes);
  222. [DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
  223. public static extern int FreeCredentialsHandle(ref SECURITY_HANDLE pCred);
  224. [DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
  225. public static extern int DeleteSecurityContext(ref SECURITY_HANDLE pCred);
  226. #endregion
  227. }
  228. [StructLayout(LayoutKind.Sequential)]
  229. struct SecBufferDesc : IDisposable
  230. {
  231. public int ulVersion;
  232. public int cBuffers;
  233. public IntPtr pBuffers; //Point to SecBuffer
  234. public SecBufferDesc(int bufferSize)
  235. {
  236. ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
  237. cBuffers = 1;
  238. SecBuffer secBuffer = new SecBuffer(bufferSize);
  239. pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer));
  240. Marshal.StructureToPtr(secBuffer, pBuffers, false);
  241. }
  242. public SecBufferDesc(byte[] secBufferBytes)
  243. {
  244. ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
  245. cBuffers = 1;
  246. SecBuffer thisSecBuffer = new SecBuffer(secBufferBytes);
  247. pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(thisSecBuffer));
  248. Marshal.StructureToPtr(thisSecBuffer, pBuffers, false);
  249. }
  250. public void Dispose()
  251. {
  252. if (pBuffers != IntPtr.Zero)
  253. {
  254. Debug.Assert(cBuffers == 1);
  255. SecBuffer ThisSecBuffer = Marshal.PtrToStructure<SecBuffer>(pBuffers);
  256. ThisSecBuffer.Dispose();
  257. Marshal.FreeHGlobal(pBuffers);
  258. pBuffers = IntPtr.Zero;
  259. }
  260. }
  261. public byte[] GetSecBufferByteArray()
  262. {
  263. byte[] Buffer = null;
  264. if (pBuffers == IntPtr.Zero)
  265. {
  266. throw new InvalidOperationException("Object has already been disposed!!!");
  267. }
  268. Debug.Assert(cBuffers == 1);
  269. SecBuffer secBuffer = Marshal.PtrToStructure<SecBuffer>(pBuffers);
  270. if (secBuffer.cbBuffer > 0)
  271. {
  272. Buffer = new byte[secBuffer.cbBuffer];
  273. Marshal.Copy(secBuffer.pvBuffer, Buffer, 0, secBuffer.cbBuffer);
  274. }
  275. return (Buffer);
  276. }
  277. }
  278. /// <summary>
  279. /// Defines the type of the security buffer.
  280. /// </summary>
  281. internal enum SecBufferType
  282. {
  283. SECBUFFER_VERSION = 0,
  284. SECBUFFER_EMPTY = 0,
  285. SECBUFFER_DATA = 1,
  286. SECBUFFER_TOKEN = 2
  287. }
  288. /// <summary>
  289. /// Defines a security handle.
  290. /// </summary>
  291. [StructLayout(LayoutKind.Sequential)]
  292. internal struct SecHandle //=PCtxtHandle
  293. {
  294. IntPtr dwLower; // ULONG_PTR translates to IntPtr not to uint
  295. IntPtr dwUpper; // this is crucial for 64-Bit Platforms
  296. }
  297. /// <summary>
  298. /// Describes a buffer allocated by a transport to pass to a security package.
  299. /// </summary>
  300. [StructLayout(LayoutKind.Sequential)]
  301. internal struct SecBuffer : IDisposable
  302. {
  303. /// <summary>
  304. /// Specifies the size, in bytes, of the buffer.
  305. /// </summary>
  306. public int cbBuffer;
  307. /// <summary>
  308. /// Bit flags that indicate the type of the buffer.
  309. /// </summary>
  310. public int BufferType;
  311. /// <summary>
  312. /// Pointer to a buffer.
  313. /// </summary>
  314. public IntPtr pvBuffer;
  315. public SecBuffer(int bufferSize)
  316. {
  317. cbBuffer = bufferSize;
  318. BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
  319. pvBuffer = Marshal.AllocHGlobal(bufferSize);
  320. }
  321. public SecBuffer(byte[] secBufferBytes)
  322. {
  323. cbBuffer = secBufferBytes.Length;
  324. BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
  325. pvBuffer = Marshal.AllocHGlobal(cbBuffer);
  326. Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
  327. }
  328. public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType)
  329. {
  330. cbBuffer = secBufferBytes.Length;
  331. BufferType = (int)bufferType;
  332. pvBuffer = Marshal.AllocHGlobal(cbBuffer);
  333. Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
  334. }
  335. public void Dispose()
  336. {
  337. if (pvBuffer != IntPtr.Zero)
  338. {
  339. Marshal.FreeHGlobal(pvBuffer);
  340. pvBuffer = IntPtr.Zero;
  341. }
  342. }
  343. }
  344. /// <summary>
  345. /// Hold a numeric value used in defining other data types.
  346. /// </summary>
  347. [StructLayout(LayoutKind.Sequential)]
  348. internal struct SECURITY_INTEGER
  349. {
  350. /// <summary>
  351. /// Least significant digits.
  352. /// </summary>
  353. public uint LowPart;
  354. /// <summary>
  355. /// Most significant digits.
  356. /// </summary>
  357. public int HighPart;
  358. public SECURITY_INTEGER(int dummy)
  359. {
  360. LowPart = 0;
  361. HighPart = 0;
  362. }
  363. };
  364. /// <summary>
  365. /// Holds a pointer used to define a security handle.
  366. /// </summary>
  367. [StructLayout(LayoutKind.Sequential)]
  368. internal struct SECURITY_HANDLE
  369. {
  370. /// <summary>
  371. /// Least significant digits.
  372. /// </summary>
  373. public IntPtr LowPart;
  374. /// <summary>
  375. /// Most significant digits.
  376. /// </summary>
  377. public IntPtr HighPart;
  378. public SECURITY_HANDLE(int dummy)
  379. {
  380. LowPart = HighPart = new IntPtr(0);
  381. }
  382. };
  383. /// <summary>
  384. /// Indicates the sizes of important structures used in the message support functions.
  385. /// </summary>
  386. [StructLayout(LayoutKind.Sequential)]
  387. internal struct SecPkgContext_Sizes
  388. {
  389. /// <summary>
  390. /// Specifies the maximum size of the security token used in the authentication changes.
  391. /// </summary>
  392. public uint cbMaxToken;
  393. /// <summary>
  394. /// Specifies the maximum size of the signature created by the <b>MakeSignature</b> function.
  395. /// This member must be zero if integrity services are not requested or available.
  396. /// </summary>
  397. public uint cbMaxSignature;
  398. /// <summary>
  399. /// Specifies the preferred integral size of the messages.
  400. /// </summary>
  401. public uint cbBlockSize;
  402. /// <summary>
  403. /// Size of the security trailer to be appended to messages.
  404. /// This member should be zero if the relevant services are not requested or available.
  405. /// </summary>
  406. public uint cbSecurityTrailer;
  407. };
  408. }
  409. #endif