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.

413 lines
12 KiB

4 years ago
  1. #if MYSQL_6_9
  2. // Copyright © 2012, 2016 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.IO;
  24. using System;
  25. using Externals.MySql.Data.MySqlClient.Properties;
  26. using Externals.MySql.Data.Common;
  27. using System.Runtime.InteropServices;
  28. using System.Diagnostics;
  29. using System.Security;
  30. namespace Externals.MySql.Data.MySqlClient.Authentication
  31. {
  32. /// <summary>
  33. ///
  34. /// </summary>
  35. [SuppressUnmanagedCodeSecurityAttribute()]
  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. int p = (int)Environment.OSVersion.Platform;
  47. if ((p == 4) || (p == 128))
  48. platform = "Unix";
  49. else if (Environment.OSVersion.Platform == PlatformID.MacOSX)
  50. platform = "Mac OS/X";
  51. if (!String.IsNullOrEmpty(platform))
  52. throw new MySqlException(String.Format(Resources.WinAuthNotSupportOnPlatform, platform));
  53. base.CheckConstraints();
  54. }
  55. public override string GetUsername()
  56. {
  57. string username = base.GetUsername();
  58. if (String.IsNullOrEmpty(username))
  59. return "auth_windows";
  60. return username;
  61. }
  62. public override string PluginName
  63. {
  64. get { return "authentication_windows_client"; }
  65. }
  66. protected override byte[] MoreData(byte[] moreData)
  67. {
  68. if (moreData == null)
  69. AcquireCredentials();
  70. byte[] clientBlob = null;
  71. if (continueProcessing)
  72. InitializeClient(out clientBlob, moreData, out continueProcessing);
  73. if (!continueProcessing || clientBlob == null || clientBlob.Length == 0)
  74. {
  75. FreeCredentialsHandle(ref outboundCredentials);
  76. DeleteSecurityContext(ref clientContext);
  77. return null;
  78. }
  79. return clientBlob;
  80. }
  81. void InitializeClient(out byte[] clientBlob, byte[] serverBlob, out bool continueProcessing)
  82. {
  83. clientBlob = null;
  84. continueProcessing = true;
  85. SecBufferDesc clientBufferDesc = new SecBufferDesc(MAX_TOKEN_SIZE);
  86. SECURITY_INTEGER initLifetime = new SECURITY_INTEGER(0);
  87. int ss = -1;
  88. try
  89. {
  90. uint ContextAttributes = 0;
  91. if (serverBlob == null)
  92. {
  93. ss = InitializeSecurityContext(
  94. ref outboundCredentials,
  95. IntPtr.Zero,
  96. targetName,
  97. STANDARD_CONTEXT_ATTRIBUTES,
  98. 0,
  99. SECURITY_NETWORK_DREP,
  100. IntPtr.Zero, /* always zero first time around */
  101. 0,
  102. out clientContext,
  103. out clientBufferDesc,
  104. out ContextAttributes,
  105. out initLifetime);
  106. }
  107. else
  108. {
  109. SecBufferDesc serverBufferDesc = new SecBufferDesc(serverBlob);
  110. try
  111. {
  112. ss = InitializeSecurityContext(ref outboundCredentials,
  113. ref clientContext,
  114. targetName,
  115. STANDARD_CONTEXT_ATTRIBUTES,
  116. 0,
  117. SECURITY_NETWORK_DREP,
  118. ref serverBufferDesc,
  119. 0,
  120. out clientContext,
  121. out clientBufferDesc,
  122. out ContextAttributes,
  123. out initLifetime);
  124. }
  125. finally
  126. {
  127. serverBufferDesc.Dispose();
  128. }
  129. }
  130. if ((SEC_I_COMPLETE_NEEDED == ss)
  131. || (SEC_I_COMPLETE_AND_CONTINUE == ss))
  132. {
  133. CompleteAuthToken(ref clientContext, ref clientBufferDesc);
  134. }
  135. if (ss != SEC_E_OK &&
  136. ss != SEC_I_CONTINUE_NEEDED &&
  137. ss != SEC_I_COMPLETE_NEEDED &&
  138. ss != SEC_I_COMPLETE_AND_CONTINUE)
  139. {
  140. throw new MySqlException(
  141. "InitializeSecurityContext() failed with errorcode " + ss);
  142. }
  143. clientBlob = clientBufferDesc.GetSecBufferByteArray();
  144. }
  145. finally
  146. {
  147. clientBufferDesc.Dispose();
  148. }
  149. continueProcessing = (ss != SEC_E_OK && ss != SEC_I_COMPLETE_NEEDED);
  150. }
  151. private void AcquireCredentials()
  152. {
  153. continueProcessing = true;
  154. int ss = AcquireCredentialsHandle(null, "Negotiate", SECPKG_CRED_OUTBOUND,
  155. IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, ref outboundCredentials,
  156. ref lifetime);
  157. if (ss != SEC_E_OK)
  158. throw new MySqlException("AcquireCredentialsHandle failed with errorcode" + ss);
  159. }
  160. #region SSPI Constants and Imports
  161. const int SEC_E_OK = 0;
  162. const int SEC_I_CONTINUE_NEEDED = 0x90312;
  163. const int SEC_I_COMPLETE_NEEDED = 0x1013;
  164. const int SEC_I_COMPLETE_AND_CONTINUE = 0x1014;
  165. const int SECPKG_CRED_OUTBOUND = 2;
  166. const int SECURITY_NETWORK_DREP = 0;
  167. const int SECURITY_NATIVE_DREP = 0x10;
  168. const int SECPKG_CRED_INBOUND = 1;
  169. const int MAX_TOKEN_SIZE = 12288;
  170. const int SECPKG_ATTR_SIZES = 0;
  171. const int STANDARD_CONTEXT_ATTRIBUTES = 0;
  172. [DllImport("secur32", CharSet = CharSet.Unicode)]
  173. static extern int AcquireCredentialsHandle(
  174. string pszPrincipal,
  175. string pszPackage,
  176. int fCredentialUse,
  177. IntPtr PAuthenticationID,
  178. IntPtr pAuthData,
  179. int pGetKeyFn,
  180. IntPtr pvGetKeyArgument,
  181. ref SECURITY_HANDLE phCredential,
  182. ref SECURITY_INTEGER ptsExpiry);
  183. [DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
  184. static extern int InitializeSecurityContext(
  185. ref SECURITY_HANDLE phCredential,
  186. IntPtr phContext,
  187. string pszTargetName,
  188. int fContextReq,
  189. int Reserved1,
  190. int TargetDataRep,
  191. IntPtr pInput,
  192. int Reserved2,
  193. out SECURITY_HANDLE phNewContext,
  194. out SecBufferDesc pOutput,
  195. out uint pfContextAttr,
  196. out SECURITY_INTEGER ptsExpiry);
  197. [DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
  198. static extern int InitializeSecurityContext(
  199. ref SECURITY_HANDLE phCredential,
  200. ref SECURITY_HANDLE phContext,
  201. string pszTargetName,
  202. int fContextReq,
  203. int Reserved1,
  204. int TargetDataRep,
  205. ref SecBufferDesc SecBufferDesc,
  206. int Reserved2,
  207. out SECURITY_HANDLE phNewContext,
  208. out SecBufferDesc pOutput,
  209. out uint pfContextAttr,
  210. out SECURITY_INTEGER ptsExpiry);
  211. [DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
  212. static extern int CompleteAuthToken(
  213. ref SECURITY_HANDLE phContext,
  214. ref SecBufferDesc pToken);
  215. [DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
  216. public static extern int QueryContextAttributes(
  217. ref SECURITY_HANDLE phContext,
  218. uint ulAttribute,
  219. out SecPkgContext_Sizes pContextAttributes);
  220. [DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
  221. public static extern int FreeCredentialsHandle(ref SECURITY_HANDLE pCred);
  222. [DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
  223. public static extern int DeleteSecurityContext(ref SECURITY_HANDLE pCred);
  224. #endregion
  225. }
  226. [StructLayout(LayoutKind.Sequential)]
  227. struct SecBufferDesc : IDisposable
  228. {
  229. public int ulVersion;
  230. public int cBuffers;
  231. public IntPtr pBuffers; //Point to SecBuffer
  232. public SecBufferDesc(int bufferSize)
  233. {
  234. ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
  235. cBuffers = 1;
  236. SecBuffer secBuffer = new SecBuffer(bufferSize);
  237. pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer));
  238. Marshal.StructureToPtr(secBuffer, pBuffers, false);
  239. }
  240. public SecBufferDesc(byte[] secBufferBytes)
  241. {
  242. ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
  243. cBuffers = 1;
  244. SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytes);
  245. pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer));
  246. Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false);
  247. }
  248. public void Dispose()
  249. {
  250. if (pBuffers != IntPtr.Zero)
  251. {
  252. Debug.Assert(cBuffers == 1);
  253. SecBuffer ThisSecBuffer =
  254. (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer));
  255. ThisSecBuffer.Dispose();
  256. Marshal.FreeHGlobal(pBuffers);
  257. pBuffers = IntPtr.Zero;
  258. }
  259. }
  260. public byte[] GetSecBufferByteArray()
  261. {
  262. byte[] Buffer = null;
  263. if (pBuffers == IntPtr.Zero)
  264. {
  265. throw new InvalidOperationException("Object has already been disposed!!!");
  266. }
  267. Debug.Assert(cBuffers == 1);
  268. SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers,
  269. typeof(SecBuffer));
  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. internal enum SecBufferType
  279. {
  280. SECBUFFER_VERSION = 0,
  281. SECBUFFER_EMPTY = 0,
  282. SECBUFFER_DATA = 1,
  283. SECBUFFER_TOKEN = 2
  284. }
  285. [StructLayout(LayoutKind.Sequential)]
  286. internal struct SecHandle //=PCtxtHandle
  287. {
  288. IntPtr dwLower; // ULONG_PTR translates to IntPtr not to uint
  289. IntPtr dwUpper; // this is crucial for 64-Bit Platforms
  290. }
  291. [StructLayout(LayoutKind.Sequential)]
  292. internal struct SecBuffer : IDisposable
  293. {
  294. public int cbBuffer;
  295. public int BufferType;
  296. public IntPtr pvBuffer;
  297. internal SecBuffer(int bufferSize)
  298. {
  299. cbBuffer = bufferSize;
  300. BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
  301. pvBuffer = Marshal.AllocHGlobal(bufferSize);
  302. }
  303. internal SecBuffer(byte[] secBufferBytes)
  304. {
  305. cbBuffer = secBufferBytes.Length;
  306. BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
  307. pvBuffer = Marshal.AllocHGlobal(cbBuffer);
  308. Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
  309. }
  310. internal SecBuffer(byte[] secBufferBytes, SecBufferType bufferType)
  311. {
  312. cbBuffer = secBufferBytes.Length;
  313. BufferType = (int)bufferType;
  314. pvBuffer = Marshal.AllocHGlobal(cbBuffer);
  315. Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
  316. }
  317. public void Dispose()
  318. {
  319. if (pvBuffer != IntPtr.Zero)
  320. {
  321. Marshal.FreeHGlobal(pvBuffer);
  322. pvBuffer = IntPtr.Zero;
  323. }
  324. }
  325. }
  326. [StructLayout(LayoutKind.Sequential)]
  327. internal struct SECURITY_INTEGER
  328. {
  329. public uint LowPart;
  330. public int HighPart;
  331. public SECURITY_INTEGER(int dummy)
  332. {
  333. LowPart = 0;
  334. HighPart = 0;
  335. }
  336. };
  337. [StructLayout(LayoutKind.Sequential)]
  338. internal struct SECURITY_HANDLE
  339. {
  340. public IntPtr LowPart;
  341. public IntPtr HighPart;
  342. public SECURITY_HANDLE(int dummy)
  343. {
  344. LowPart = HighPart = new IntPtr(0);
  345. }
  346. };
  347. [StructLayout(LayoutKind.Sequential)]
  348. internal struct SecPkgContext_Sizes
  349. {
  350. public uint cbMaxToken;
  351. public uint cbMaxSignature;
  352. public uint cbBlockSize;
  353. public uint cbSecurityTrailer;
  354. };
  355. }
  356. #endif