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

#if MYSQL_6_9
// Copyright © 2012, 2016 Oracle and/or its affiliates. All rights reserved.
//
// MySQL Connector/NET is licensed under the terms of the GPLv2
// <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
// MySQL Connectors. There are special exceptions to the terms and
// conditions of the GPLv2 as it is applied to this software, see the
// FLOSS License Exception
// <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
using System.IO;
using System;
using Externals.MySql.Data.MySqlClient.Properties;
using Externals.MySql.Data.Common;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Security;
namespace Externals.MySql.Data.MySqlClient.Authentication
{
/// <summary>
///
/// </summary>
[SuppressUnmanagedCodeSecurityAttribute()]
internal class MySqlWindowsAuthenticationPlugin : MySqlAuthenticationPlugin
{
SECURITY_HANDLE outboundCredentials = new SECURITY_HANDLE(0);
SECURITY_HANDLE clientContext = new SECURITY_HANDLE(0);
SECURITY_INTEGER lifetime = new SECURITY_INTEGER(0);
bool continueProcessing;
string targetName = null;
protected override void CheckConstraints()
{
string platform = String.Empty;
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128))
platform = "Unix";
else if (Environment.OSVersion.Platform == PlatformID.MacOSX)
platform = "Mac OS/X";
if (!String.IsNullOrEmpty(platform))
throw new MySqlException(String.Format(Resources.WinAuthNotSupportOnPlatform, platform));
base.CheckConstraints();
}
public override string GetUsername()
{
string username = base.GetUsername();
if (String.IsNullOrEmpty(username))
return "auth_windows";
return username;
}
public override string PluginName
{
get { return "authentication_windows_client"; }
}
protected override byte[] MoreData(byte[] moreData)
{
if (moreData == null)
AcquireCredentials();
byte[] clientBlob = null;
if (continueProcessing)
InitializeClient(out clientBlob, moreData, out continueProcessing);
if (!continueProcessing || clientBlob == null || clientBlob.Length == 0)
{
FreeCredentialsHandle(ref outboundCredentials);
DeleteSecurityContext(ref clientContext);
return null;
}
return clientBlob;
}
void InitializeClient(out byte[] clientBlob, byte[] serverBlob, out bool continueProcessing)
{
clientBlob = null;
continueProcessing = true;
SecBufferDesc clientBufferDesc = new SecBufferDesc(MAX_TOKEN_SIZE);
SECURITY_INTEGER initLifetime = new SECURITY_INTEGER(0);
int ss = -1;
try
{
uint ContextAttributes = 0;
if (serverBlob == null)
{
ss = InitializeSecurityContext(
ref outboundCredentials,
IntPtr.Zero,
targetName,
STANDARD_CONTEXT_ATTRIBUTES,
0,
SECURITY_NETWORK_DREP,
IntPtr.Zero, /* always zero first time around */
0,
out clientContext,
out clientBufferDesc,
out ContextAttributes,
out initLifetime);
}
else
{
SecBufferDesc serverBufferDesc = new SecBufferDesc(serverBlob);
try
{
ss = InitializeSecurityContext(ref outboundCredentials,
ref clientContext,
targetName,
STANDARD_CONTEXT_ATTRIBUTES,
0,
SECURITY_NETWORK_DREP,
ref serverBufferDesc,
0,
out clientContext,
out clientBufferDesc,
out ContextAttributes,
out initLifetime);
}
finally
{
serverBufferDesc.Dispose();
}
}
if ((SEC_I_COMPLETE_NEEDED == ss)
|| (SEC_I_COMPLETE_AND_CONTINUE == ss))
{
CompleteAuthToken(ref clientContext, ref clientBufferDesc);
}
if (ss != SEC_E_OK &&
ss != SEC_I_CONTINUE_NEEDED &&
ss != SEC_I_COMPLETE_NEEDED &&
ss != SEC_I_COMPLETE_AND_CONTINUE)
{
throw new MySqlException(
"InitializeSecurityContext() failed with errorcode " + ss);
}
clientBlob = clientBufferDesc.GetSecBufferByteArray();
}
finally
{
clientBufferDesc.Dispose();
}
continueProcessing = (ss != SEC_E_OK && ss != SEC_I_COMPLETE_NEEDED);
}
private void AcquireCredentials()
{
continueProcessing = true;
int ss = AcquireCredentialsHandle(null, "Negotiate", SECPKG_CRED_OUTBOUND,
IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, ref outboundCredentials,
ref lifetime);
if (ss != SEC_E_OK)
throw new MySqlException("AcquireCredentialsHandle failed with errorcode" + ss);
}
#region SSPI Constants and Imports
const int SEC_E_OK = 0;
const int SEC_I_CONTINUE_NEEDED = 0x90312;
const int SEC_I_COMPLETE_NEEDED = 0x1013;
const int SEC_I_COMPLETE_AND_CONTINUE = 0x1014;
const int SECPKG_CRED_OUTBOUND = 2;
const int SECURITY_NETWORK_DREP = 0;
const int SECURITY_NATIVE_DREP = 0x10;
const int SECPKG_CRED_INBOUND = 1;
const int MAX_TOKEN_SIZE = 12288;
const int SECPKG_ATTR_SIZES = 0;
const int STANDARD_CONTEXT_ATTRIBUTES = 0;
[DllImport("secur32", CharSet = CharSet.Unicode)]
static extern int AcquireCredentialsHandle(
string pszPrincipal,
string pszPackage,
int fCredentialUse,
IntPtr PAuthenticationID,
IntPtr pAuthData,
int pGetKeyFn,
IntPtr pvGetKeyArgument,
ref SECURITY_HANDLE phCredential,
ref SECURITY_INTEGER ptsExpiry);
[DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int InitializeSecurityContext(
ref SECURITY_HANDLE phCredential,
IntPtr phContext,
string pszTargetName,
int fContextReq,
int Reserved1,
int TargetDataRep,
IntPtr pInput,
int Reserved2,
out SECURITY_HANDLE phNewContext,
out SecBufferDesc pOutput,
out uint pfContextAttr,
out SECURITY_INTEGER ptsExpiry);
[DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int InitializeSecurityContext(
ref SECURITY_HANDLE phCredential,
ref SECURITY_HANDLE phContext,
string pszTargetName,
int fContextReq,
int Reserved1,
int TargetDataRep,
ref SecBufferDesc SecBufferDesc,
int Reserved2,
out SECURITY_HANDLE phNewContext,
out SecBufferDesc pOutput,
out uint pfContextAttr,
out SECURITY_INTEGER ptsExpiry);
[DllImport("secur32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int CompleteAuthToken(
ref SECURITY_HANDLE phContext,
ref SecBufferDesc pToken);
[DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
public static extern int QueryContextAttributes(
ref SECURITY_HANDLE phContext,
uint ulAttribute,
out SecPkgContext_Sizes pContextAttributes);
[DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
public static extern int FreeCredentialsHandle(ref SECURITY_HANDLE pCred);
[DllImport("secur32.Dll", CharSet = CharSet.Unicode, SetLastError = false)]
public static extern int DeleteSecurityContext(ref SECURITY_HANDLE pCred);
#endregion
}
[StructLayout(LayoutKind.Sequential)]
struct SecBufferDesc : IDisposable
{
public int ulVersion;
public int cBuffers;
public IntPtr pBuffers; //Point to SecBuffer
public SecBufferDesc(int bufferSize)
{
ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
cBuffers = 1;
SecBuffer secBuffer = new SecBuffer(bufferSize);
pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer));
Marshal.StructureToPtr(secBuffer, pBuffers, false);
}
public SecBufferDesc(byte[] secBufferBytes)
{
ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
cBuffers = 1;
SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytes);
pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer));
Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false);
}
public void Dispose()
{
if (pBuffers != IntPtr.Zero)
{
Debug.Assert(cBuffers == 1);
SecBuffer ThisSecBuffer =
(SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer));
ThisSecBuffer.Dispose();
Marshal.FreeHGlobal(pBuffers);
pBuffers = IntPtr.Zero;
}
}
public byte[] GetSecBufferByteArray()
{
byte[] Buffer = null;
if (pBuffers == IntPtr.Zero)
{
throw new InvalidOperationException("Object has already been disposed!!!");
}
Debug.Assert(cBuffers == 1);
SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers,
typeof(SecBuffer));
if (secBuffer.cbBuffer > 0)
{
Buffer = new byte[secBuffer.cbBuffer];
Marshal.Copy(secBuffer.pvBuffer, Buffer, 0, secBuffer.cbBuffer);
}
return (Buffer);
}
}
internal enum SecBufferType
{
SECBUFFER_VERSION = 0,
SECBUFFER_EMPTY = 0,
SECBUFFER_DATA = 1,
SECBUFFER_TOKEN = 2
}
[StructLayout(LayoutKind.Sequential)]
internal struct SecHandle //=PCtxtHandle
{
IntPtr dwLower; // ULONG_PTR translates to IntPtr not to uint
IntPtr dwUpper; // this is crucial for 64-Bit Platforms
}
[StructLayout(LayoutKind.Sequential)]
internal struct SecBuffer : IDisposable
{
public int cbBuffer;
public int BufferType;
public IntPtr pvBuffer;
internal SecBuffer(int bufferSize)
{
cbBuffer = bufferSize;
BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
pvBuffer = Marshal.AllocHGlobal(bufferSize);
}
internal SecBuffer(byte[] secBufferBytes)
{
cbBuffer = secBufferBytes.Length;
BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
pvBuffer = Marshal.AllocHGlobal(cbBuffer);
Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
}
internal SecBuffer(byte[] secBufferBytes, SecBufferType bufferType)
{
cbBuffer = secBufferBytes.Length;
BufferType = (int)bufferType;
pvBuffer = Marshal.AllocHGlobal(cbBuffer);
Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
}
public void Dispose()
{
if (pvBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(pvBuffer);
pvBuffer = IntPtr.Zero;
}
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_INTEGER
{
public uint LowPart;
public int HighPart;
public SECURITY_INTEGER(int dummy)
{
LowPart = 0;
HighPart = 0;
}
};
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_HANDLE
{
public IntPtr LowPart;
public IntPtr HighPart;
public SECURITY_HANDLE(int dummy)
{
LowPart = HighPart = new IntPtr(0);
}
};
[StructLayout(LayoutKind.Sequential)]
internal struct SecPkgContext_Sizes
{
public uint cbMaxToken;
public uint cbMaxSignature;
public uint cbBlockSize;
public uint cbSecurityTrailer;
};
}
#endif