List:Commits« Previous MessageNext Message »
From:Reggie Burnett Date:March 31 2011 5:22pm
Subject:bzr commit into connector-net-trunk branch (reggie.burnett:963)
View as plain text  
#At file:///C:/Users/Reggie/work/connector-net/trunk-windows-auth/ based on revid:reggie.burnett@stripped22195247-c5d6si4vwuh6rl81

  963 Reggie Burnett	2011-03-31 [merge]
      Windows authenitcation plugin in Connector/NET
      
      This patch implements IntegratedSecurity=yes (windows authentication) 
      for mysql servers that have the corresponding plugin.

    added:
      MySql.Data/Provider/Source/SSPI.cs
    modified:
      MySql.Data/Provider/MySql.Data.CF.csproj
      MySql.Data/Provider/MySql.Data.csproj
      MySql.Data/Provider/Source/Connection.cs
      MySql.Data/Provider/Source/Driver.cs
      MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs
      MySql.Data/Provider/Source/MySqlPacket.cs
      MySql.Data/Provider/Source/MySqlPoolManager.cs
      MySql.Data/Provider/Source/MySqlStream.cs
      MySql.Data/Provider/Source/MysqlDefs.cs
      MySql.Data/Provider/Source/NativeDriver.cs
      MySql.Data/Tests/Source/ConnectionTests.cs
=== modified file 'MySql.Data/Provider/MySql.Data.CF.csproj'
=== modified file 'MySql.Data/Provider/MySql.Data.CF.csproj'
--- a/MySql.Data/Provider/MySql.Data.CF.csproj	2010-02-13 04:34:16 +0000
+++ b/MySql.Data/Provider/MySql.Data.CF.csproj	2010-10-14 22:10:08 +0000
@@ -152,6 +152,7 @@
     <Compile Include="Source\zlib\ZOutputStream.cs" />
     <Compile Include="Source\zlib\ZStream.cs" />
     <Compile Include="Source\zlib\ZStreamException.cs" />
+    <Compile Include="SSPI.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Properties\Resources.resx">

=== modified file 'MySql.Data/Provider/MySql.Data.csproj'
--- a/MySql.Data/Provider/MySql.Data.csproj	2010-12-22 21:21:41 +0000
+++ b/MySql.Data/Provider/MySql.Data.csproj	2011-03-31 17:21:49 +0000
@@ -173,6 +173,7 @@
     <Compile Include="Source\MySqlScript.cs" />
     <Compile Include="Source\Types\MySqlGuid.cs" />
     <Compile Include="Source\ResultSet.cs" />
+    <Compile Include="Source\SSPI.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Properties\ReservedWords.txt" />

=== modified file 'MySql.Data/Provider/Source/Connection.cs'
--- a/MySql.Data/Provider/Source/Connection.cs	2011-02-18 15:26:03 +0000
+++ b/MySql.Data/Provider/Source/Connection.cs	2011-03-31 17:21:49 +0000
@@ -466,6 +466,7 @@
                     if (driver == null)
                         driver = pool.GetConnection();
                     procedureCache = pool.ProcedureCache;
+                    
                 }
                 else
                 {

=== modified file 'MySql.Data/Provider/Source/Driver.cs'
--- a/MySql.Data/Provider/Source/Driver.cs	2011-02-21 20:49:04 +0000
+++ b/MySql.Data/Provider/Source/Driver.cs	2011-03-31 17:21:49 +0000
@@ -39,7 +39,6 @@
     {
         protected Encoding encoding;
         protected MySqlConnectionStringBuilder connectionString;
-        protected ClientFlags serverCaps;
         protected bool isOpen;
         protected DateTime creationTime;
         protected string serverCharSet;

=== modified file 'MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs'
--- a/MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs	2011-03-03 18:03:34 +0000
+++ b/MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs	2011-03-31 17:21:49 +0000
@@ -377,6 +377,27 @@
                 SetValue("Certificate Thumbprint", value);
             }
         }
+
+        [Category("Authentication")]
+        [DisplayName("Integrated Security")]
+        [Description("Use windows authentication when connecting to server")]
+        [DefaultValue(false)]
+        public bool IntegratedSecurity
+        {
+            get 
+            {
+                object val = values["Integrated Security"];
+                return (bool)val;
+            }
+            set
+            {
+                if (!MySql.Data.Common.Platform.IsWindows())
+                    throw new MySqlException("IntegratedSecurity is supported on Windows only");
+
+                SetValue("Integrated Security", value);
+            }
+        }
+
         #endregion
 
         #region Other Properties
@@ -887,6 +908,10 @@
                 string s = value.ToString().ToLower(CultureInfo.InvariantCulture);
                 if (s == "yes" || s == "true") return true;
                 if (s == "no" || s == "false") return false;
+
+                // Unclean, but we need IntegratedSecurity=SSPI to be
+                // the same as IntegratedSecurity=true, to match SqlClient
+                if (s == "sspi") return true;
                 throw new FormatException(String.Format(Resources.InvalidValueForBoolean, value));
             }
             else

=== modified file 'MySql.Data/Provider/Source/MySqlPacket.cs'
--- a/MySql.Data/Provider/Source/MySqlPacket.cs	2010-11-30 18:33:23 +0000
+++ b/MySql.Data/Provider/Source/MySqlPacket.cs	2011-03-31 17:21:49 +0000
@@ -338,7 +338,7 @@
             return encoding.GetString(tempBuffer, 0, (int)length);
         }
 
-        public string ReadString()
+        public string ReadString(Encoding enc)
         {
             byte[] bits = buffer.GetBuffer();
             int end = (int)buffer.Position;
@@ -347,12 +347,16 @@
                 bits[end] != 0 && (int)bits[end] != -1)
                 end++;
 
-            string s = encoding.GetString(bits, 
+            string s = enc.GetString(bits, 
                 (int)buffer.Position, end - (int)buffer.Position);
             buffer.Position = end + 1;
             return s;
         }
 
+        public string ReadString()
+        {
+            return ReadString(this.encoding);
+        }
         #endregion        
     }
 }

=== modified file 'MySql.Data/Provider/Source/MySqlPoolManager.cs'
--- a/MySql.Data/Provider/Source/MySqlPoolManager.cs	2010-08-18 19:48:34 +0000
+++ b/MySql.Data/Provider/Source/MySqlPoolManager.cs	2010-10-14 22:10:08 +0000
@@ -27,6 +27,8 @@
 
 namespace MySql.Data.MySqlClient
 {
+
+
     /// <summary>
     /// Summary description for MySqlPoolManager.
     /// </summary>
@@ -43,9 +45,44 @@
         private static Timer timer = new Timer(new TimerCallback(CleanIdleConnections),
             null, maxConnectionIdleTime*1000, maxConnectionIdleTime*1000);
 
+        private static string GetKey(MySqlConnectionStringBuilder settings)
+        {
+            string key  = settings.ConnectionString;
+#if !CF
+            if(settings.IntegratedSecurity && !settings.ConnectionReset)
+            {
+                try
+                {
+                    // Append SID to the connection string to generate a key
+                    // With Integrated security different Windows users with the same
+                    // connection string may be mapped to different MySQL accounts.
+                    System.Security.Principal.WindowsIdentity id =
+                        System.Security.Principal.WindowsIdentity.GetCurrent();
+                    if (id != null)
+                    {
+                        key += ";" + id.User;
+                    }
+                }
+                catch (System.Security.SecurityException ex)
+                {
+                    // Documentation for WindowsIdentity.GetCurrent() states 
+                    // SecurityException can be thrown. In this case the 
+                    // connection can only be pooled if reset is done.
+                    throw new MySqlException(
+                        "Cannot retrieve Windows identity for current user " +
+                        "authentication using IntegratedSecurity" +
+                        "Connections that use  IntegratedSecurity cannot be " + 
+                        "pooled. Use either 'ConnectionReset=true' or " +
+                        "'Pooling=false' in the connection string" +
+                        "to fix", ex );
+                }
+            }
+#endif
+            return key;
+        }
         public static MySqlPool GetPool(MySqlConnectionStringBuilder settings)
         {
-            string text = settings.ConnectionString;
+            string text = GetKey(settings);
 
             lock (pools.SyncRoot)
             {
@@ -86,8 +123,17 @@
         public static void ClearPool(MySqlConnectionStringBuilder settings)
         {
             Debug.Assert(settings != null);
-
-            string text = settings.ConnectionString;
+            string text;
+            try
+            {
+                text = GetKey(settings);
+            }
+            catch (MySqlException)
+            {
+                // Cannot retrieve windows identity for IntegratedSecurity=true
+                // This can be ignored.
+                return;
+            }
             ClearPoolByText(text);
         }
 

=== modified file 'MySql.Data/Provider/Source/MySqlStream.cs'
--- a/MySql.Data/Provider/Source/MySqlStream.cs	2010-08-18 19:48:34 +0000
+++ b/MySql.Data/Provider/Source/MySqlStream.cs	2010-10-14 22:10:08 +0000
@@ -44,7 +44,13 @@
         Stream inStream;
         Stream outStream;
 
-
+        internal Stream BaseStream
+        {
+            get
+            {
+                return timedStream;
+            }
+        }
 		public MySqlStream(Encoding encoding)
 		{
 			// we have no idea what the real value is so we start off with the max value

=== modified file 'MySql.Data/Provider/Source/MysqlDefs.cs'
--- a/MySql.Data/Provider/Source/MysqlDefs.cs	2010-08-18 19:48:34 +0000
+++ b/MySql.Data/Provider/Source/MysqlDefs.cs	2010-10-14 22:10:08 +0000
@@ -48,7 +48,10 @@
         SECURE_CONNECTION = 32768,      // new 4.1 authentication
         MULTI_STATEMENTS = 65536,       // Allow multi-stmt support
         MULTI_RESULTS = 131072,         // Allow multiple resultsets
-        PS_MULTI_RESULTS = 1UL << 18    // allow multi results using PS protocol
+        PS_MULTI_RESULTS = 1UL << 18,    // allow multi results using PS protocol
+        PLUGIN_AUTH = (1UL << 19), //Client supports plugin authentication
+        CLIENT_SSL_VERIFY_SERVER_CERT= (1UL << 30),
+        CLIENT_REMEMBER_OPTIONS= (1UL << 31)
     }
 
     [Flags]

=== modified file 'MySql.Data/Provider/Source/NativeDriver.cs'
--- a/MySql.Data/Provider/Source/NativeDriver.cs	2011-02-15 21:07:24 +0000
+++ b/MySql.Data/Provider/Source/NativeDriver.cs	2011-03-31 17:21:49 +0000
@@ -33,7 +33,6 @@
 using System.Net.Security;
 using System.Security.Authentication;
 using System.Globalization;
-using System.Text;
 #endif
 
 namespace MySql.Data.MySqlClient
@@ -55,6 +54,13 @@
         private Driver owner;
         private int warnings;
 
+        // Windows authentication method string, used by the protocol.
+        // Also known as "client plugin name".
+        const string AuthenticationWindowsPlugin = "authentication_windows_client";
+
+        // Predefined username for IntegratedSecurity
+        const string AuthenticationWindowsUser = "auth_windows";
+
         public NativeDriver(Driver owner)
         {
             this.owner = owner;
@@ -115,7 +121,10 @@
                     packet = stream.ReadPacket();
                 byte marker = (byte) packet.ReadByte();
                 if (marker != 0)
+                {
+                    string s = packet.ReadString();
                     throw new MySqlException("Out of sync with server", true, null);
+                }
 
                 packet.ReadFieldLength(); /* affected rows */
                 packet.ReadFieldLength(); /* last insert id */
@@ -219,10 +228,22 @@
             owner.ConnectionCharSetIndex = (int)packet.ReadByte();
 
             serverStatus = (ServerStatusFlags) packet.ReadInteger(2);
-            packet.Position += 13;
+            uint serverCapsHigh = (uint)packet.ReadInteger(2);
+            serverCaps |= (ClientFlags)(serverCapsHigh << 16);
+           
+
+            int scrambleLength = (int)packet.ReadByte();
+
+            packet.Position += 10;
             string seedPart2 = packet.ReadString();
             encryptionSeed += seedPart2;
 
+            string authenticationMethod = ""; 
+            if ((serverCaps & ClientFlags.PLUGIN_AUTH)!=0)
+            {
+                authenticationMethod = packet.ReadString();
+            }
+            
             // based on our settings, set our connection flags
             SetConnectionFlags(serverCaps);
 
@@ -254,7 +275,7 @@
             packet.WriteByte(8);
             packet.Write(new byte[23]);
 
-            Authenticate();
+            Authenticate(false);
 
             // if we are using compression, then we use our CompressedStream class
             // to hide the ugliness of managing the compression
@@ -416,16 +437,73 @@
                 flags |= ClientFlags.SSL;
 
             // if the server supports output parameters, then we do too
-            //if ((serverCaps & ClientFlags.PS_MULTI_RESULTS) != 0)
+            if ((serverCaps & ClientFlags.PS_MULTI_RESULTS) != 0)
                 flags |= ClientFlags.PS_MULTI_RESULTS;
 
+            if(Settings.IntegratedSecurity)
+            {
+                if ((serverCaps & ClientFlags.PLUGIN_AUTH) != 0)
+                    flags |= ClientFlags.PLUGIN_AUTH;
+            }
             connectionFlags = flags;
         }
 
+
+        private void AuthenticateSSPI()
+        {
+
+            string targetName = ""; // target name (required by Kerberos)
+
+            // First packet sent by server should include target name (for
+            // Kerberos) as UTF8 string. It might however be prepended by junk 
+            // at the start of the string (0xfe"authentication_win_client"\0,
+            // see Bug#57442), this junk will be ignored. Target name can also 
+            // be an empty string if server is not running in a domain environment, 
+            // in this case authentication will fallback to NTLM.
+
+            // Note that 0xfe byte at the start could also indicate that windows
+            // authentication is not supported by sΘrver, we throw an exception
+            // if this happens.
+
+            packet = stream.ReadPacket();
+            byte b = packet.ReadByte();
+            if (b == 0xfe)
+            {
+                string authMethod = packet.ReadString();
+                if (authMethod.Equals(AuthenticationWindowsPlugin))
+                {
+                    targetName = packet.ReadString(Encoding.UTF8);
+                }
+                else
+                {
+                    // User has requested Windows authentication,  bail out.
+                    throw new MySqlException("unexpected authentication method " +
+                        authMethod);
+                }
+            }
+            else
+            {
+                targetName = Encoding.UTF8.GetString(packet.Buffer, 0, packet.Buffer.Length);
+            }
+
+            // Do SSPI authentication handshake
+            SSPI sspi = new SSPI(targetName, stream.BaseStream);
+            sspi.AuthenticateClient();
+
+            // read ok packet.
+            packet = stream.ReadPacket();
+            ReadOk(false);
+        }
+
         /// <summary>
         /// Perform an authentication against a 4.1.1 server
+        /// <param name="reset">
+        /// True, if this function is called as part of CHANGE_USER request
+        /// (connection reset)
+        /// False, for first-time logon
+        /// </param>
         /// </summary>
-        private void AuthenticateNew()
+        private void AuthenticateNew(bool reset)
         {
             if ((connectionFlags & ClientFlags.SECURE_CONNECTION) == 0)
                 AuthenticateOld();
@@ -436,7 +514,24 @@
             else
                 packet.WriteString(""); // Add a null termination to the string.
 
-            stream.SendPacket(packet);
+            
+            if (Settings.IntegratedSecurity)
+            {
+                // Append authentication method after the database name in the 
+                // handshake authentication packet. Omit it, if we do connection
+                // reset (reset should use the same authentication method)
+                if (!reset)
+                {
+                    packet.WriteString(AuthenticationWindowsPlugin);
+                }
+                stream.SendPacket(packet);
+                AuthenticateSSPI();
+                return;
+            }
+            else
+            {
+                stream.SendPacket(packet);
+            }
 
             // this result means the server wants us to send the password using
             // old encryption
@@ -449,8 +544,7 @@
                 stream.SendPacket(packet);
                 ReadOk(true);
             }
-            else
-                ReadOk(false);
+            ReadOk(false);
         }
 
         private void AuthenticateOld()
@@ -464,11 +558,19 @@
             ReadOk(true);
         }
 
-        public void Authenticate()
+
+        public void Authenticate(bool reset)
         {
-            // write the user id to the auth packet
-            packet.WriteString(Settings.UserID);
-            AuthenticateNew();
+            if (Settings.IntegratedSecurity)
+            {
+                packet.WriteString(AuthenticationWindowsUser);
+            }
+            else
+            {
+              // write the user id to the auth packet
+              packet.WriteString(Settings.UserID);
+            }
+            AuthenticateNew(reset);
         }
 
         #endregion
@@ -480,7 +582,7 @@
             stream.SequenceByte = 0;
             packet.Clear();
             packet.WriteByte((byte)DBCmd.CHANGE_USER);
-            Authenticate();
+            Authenticate(true);
         }
 
         /// <summary>

=== added file 'MySql.Data/Provider/Source/SSPI.cs'
--- a/MySql.Data/Provider/Source/SSPI.cs	1970-01-01 00:00:00 +0000
+++ b/MySql.Data/Provider/Source/SSPI.cs	2010-10-14 22:10:08 +0000
@@ -0,0 +1,425 @@
+// Copyright (c) 2010 Oracle and its affiliates.
+//
+// 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.Collections;
+using System.Security.Principal;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Net.Sockets;
+
+using HANDLE = System.IntPtr;
+using System;
+using System.IO;
+
+
+namespace MySql.Data.MySqlClient
+{
+    internal class SSPI
+    {
+        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;
+
+        SECURITY_HANDLE outboundCredentials = new SECURITY_HANDLE(0);
+        SECURITY_HANDLE clientContext = new SECURITY_HANDLE(0);
+        Stream stream;
+        String targetName;
+        byte[] packetHeader;
+        int seq = 3;
+
+
+        [DllImport("secur32", CharSet = CharSet.Auto)]
+        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.Auto, 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.Auto, 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.Auto, SetLastError = true)]
+        static extern int CompleteAuthToken(
+            ref SECURITY_HANDLE phContext,
+            ref SecBufferDesc pToken );
+
+        [DllImport("secur32.Dll", CharSet = CharSet.Auto, SetLastError = false)]
+        public static extern int QueryContextAttributes(
+            ref SECURITY_HANDLE phContext,
+            uint ulAttribute,
+            out SecPkgContext_Sizes pContextAttributes);
+
+        [DllImport("secur32.Dll", CharSet = CharSet.Auto, SetLastError = false)]
+        public static extern int FreeCredentialsHandle(ref SECURITY_HANDLE pCred);
+
+        [DllImport("secur32.Dll", CharSet = CharSet.Auto, SetLastError = false)]
+        public static extern int DeleteSecurityContext(ref SECURITY_HANDLE pCred);
+
+        public SSPI(string targetName, Stream stream)
+        {
+            this.targetName = null;
+            this.stream = stream;
+            packetHeader = new byte[4];
+           
+        }
+
+
+        // Read MySQL packet
+        // since SSPI blobs data cannot be larger than ~12K,
+        // handling just single packet is sufficient
+        private byte[] ReadData()
+        {
+            byte[] buffer;
+            MySqlStream.ReadFully(stream, packetHeader, 0, 4);
+            int length = (int)(packetHeader[0] + (packetHeader[1] << 8) +
+                (packetHeader[2] << 16));
+            seq = packetHeader[3]+1;
+            buffer = new byte[length];
+            MySqlStream.ReadFully(stream, buffer, 0, length);
+
+            return buffer;
+        }
+
+        // Write MySQL packet
+        private void WriteData(byte[] buffer)
+        {
+            int count = buffer.Length;
+
+            packetHeader[0] = (byte)(count & 0xff);
+            packetHeader[1] = (byte)((count >> 8) & 0xff);
+            packetHeader[2] = (byte)((count >> 16) & 0xff);
+            packetHeader[3] = (byte)(seq);
+            stream.Write(packetHeader, 0, 4);
+            stream.Write(buffer, 0, count);
+            stream.Flush();
+        }
+
+        public void AuthenticateClient()
+        {
+            bool continueProcessing = true;
+            byte[] clientBlob = null;
+            byte[] serverBlob = null;
+            SECURITY_INTEGER lifetime = new SECURITY_INTEGER(0);
+            int ss;
+
+            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);
+            }
+            try
+            {
+                while (continueProcessing)
+                {
+                    InitializeClient(out clientBlob, serverBlob,
+                        out continueProcessing);
+                    if (clientBlob != null && clientBlob.Length > 0)
+                    {
+                        WriteData(clientBlob);
+                        if (continueProcessing)
+                            serverBlob = ReadData();
+                    }
+                }
+            }
+            finally
+            {
+                FreeCredentialsHandle(ref outboundCredentials);
+                DeleteSecurityContext(ref clientContext);
+            }
+        }
+
+
+        void InitializeClient(out byte[] clientBlob, byte[] serverBlob, 
+            out bool continueProcessing)
+        {
+            clientBlob = null;
+            continueProcessing = true;
+            SecBufferDesc clientBufferDesc = new SecBufferDesc(MAX_TOKEN_SIZE); 
+            SECURITY_INTEGER lifetime = 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 lifetime); 
+
+                }
+                else
+                {
+                    String s = System.Text.Encoding.UTF8.GetString(serverBlob);
+                    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 lifetime);
+                    }
+                    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);
+        }
+    }
+
+
+    [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);
+        }
+
+    }
+
+    public enum SecBufferType
+    {
+        SECBUFFER_VERSION = 0,
+        SECBUFFER_EMPTY = 0,
+        SECBUFFER_DATA = 1,
+        SECBUFFER_TOKEN = 2
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public 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)]
+    public struct SecBuffer : IDisposable
+    {
+        public int cbBuffer;
+        public int BufferType;
+        public IntPtr pvBuffer;
+
+
+        public SecBuffer(int bufferSize)
+        {
+            cbBuffer = bufferSize;
+            BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
+            pvBuffer = Marshal.AllocHGlobal(bufferSize);
+        }
+
+        public SecBuffer(byte[] secBufferBytes)
+        {
+            cbBuffer = secBufferBytes.Length;
+            BufferType = (int)SecBufferType.SECBUFFER_TOKEN;
+            pvBuffer = Marshal.AllocHGlobal(cbBuffer);
+            Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer);
+        }
+
+        public 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)]
+    public struct SECURITY_INTEGER
+    {
+        public uint LowPart;
+        public int HighPart;
+        public SECURITY_INTEGER(int dummy)
+        {
+            LowPart = 0;
+            HighPart = 0;
+        }
+    };
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct SECURITY_HANDLE
+    {
+        public uint LowPart;
+        public uint HighPart;
+        public SECURITY_HANDLE(int dummy)
+        {
+            LowPart = HighPart = 0;
+        }
+    };
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct SecPkgContext_Sizes
+    {
+        public uint cbMaxToken;
+        public uint cbMaxSignature;
+        public uint cbBlockSize;
+        public uint cbSecurityTrailer;
+    };
+
+}
+

=== modified file 'MySql.Data/Tests/Source/ConnectionTests.cs'
--- a/MySql.Data/Tests/Source/ConnectionTests.cs	2010-08-18 19:52:04 +0000
+++ b/MySql.Data/Tests/Source/ConnectionTests.cs	2010-10-14 22:10:08 +0000
@@ -69,6 +69,120 @@
             Assert.AreEqual(System.Data.ConnectionState.Closed, c.State, "State");
         }
 
+#if !CF  //No Security.Principal on CF
+
+        [Test]
+        public void TestIntegratedSecurity()
+        {
+
+
+            const string PluginName = "authentication_windows";
+
+
+            // Check if server has windows authentication plugin is installed
+            MySqlCommand cmd = new MySqlCommand("show plugins", rootConn);
+            bool haveWindowsAuthentication = false;
+            using (MySqlDataReader r = cmd.ExecuteReader())
+            {
+                while (r.Read())
+                {
+                    string name = (string)r["Name"];
+                    if (name == PluginName)
+                    {
+                        haveWindowsAuthentication = true;
+                        break;
+                    }
+                }
+            }
+            if(!haveWindowsAuthentication)
+                return;
+
+            bool haveAuthWindowsUser = false;
+            string pluginName = null;
+            string authenticationString = "";
+
+            // Check if predefined proxy user exists
+            cmd.CommandText =
+                "select plugin,authentication_string from mysql.user where user='auth_windows'";
+            using (MySqlDataReader r = cmd.ExecuteReader())
+            {
+                if (r.Read())
+                {
+                    haveAuthWindowsUser = true;
+                    pluginName = (string) r["plugin"];
+                    authenticationString = (string) r["authentication_string"];
+                }
+            }
+
+            // Create mapping for current Windows user=>foo_user
+            String windowsUser = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
+            windowsUser = windowsUser.Replace("\\", "\\\\");
+            string userMapping = windowsUser+"=foo_user";
+
+            try
+            {
+                if (!haveAuthWindowsUser)
+                {
+                    suExecSQL(
+                        "CREATE USER auth_windows IDENTIFIED WITH " + PluginName +" as '" +
+                         userMapping + "'");
+                }
+                else
+                {
+                    // extend mapping string for current user
+                    suExecSQL(
+                        "UPDATE mysql.user SET authentication_string='" + userMapping +
+                        "," + authenticationString + "'");
+                }
+                suExecSQL("create user foo_user identified by 'pass'");
+                suExecSQL("grant all privileges on *.* to 'foo_user'@'%'");
+                suExecSQL("grant proxy on foo_user to auth_windows");
+
+
+                // Finally, use IntegratedSecurity=true for the newly created user
+                string connStr = GetConnectionString(true) + ";Integrated Security=SSPI";
+                using (MySqlConnection c = new MySqlConnection(connStr))
+                {
+                    c.Open();
+
+                    MySqlCommand command = new MySqlCommand("SELECT 1", c);
+                    long ret = (long)command.ExecuteScalar();
+                    Assert.AreEqual(ret, 1);
+
+
+                    command.CommandText = "select user()";
+                    string user = (string)command.ExecuteScalar();
+                    // Check if proxy user is correct
+                    Assert.IsTrue(user.StartsWith("auth_windows@"));
+
+                    // check if mysql user is correct 
+                    // (foo_user is mapped to current  OS user)
+                    command.CommandText = "select current_user()";
+                    string currentUser = (string)command.ExecuteScalar();
+                    Assert.IsTrue(currentUser.StartsWith("foo_user@"));
+                }
+            }
+            finally
+            {
+                // Cleanup
+
+                // Drop test user
+                suExecSQL("drop user foo_user");
+                if (!haveAuthWindowsUser)
+                {
+                    // drop proxy user if we created it
+                    suExecSQL("drop user auth_windows");
+                }
+                else
+                {
+                    // revert changes in the mapping string
+                    suExecSQL("UPDATE mysql.user SET authentication_string='" +
+                        authenticationString + "'");
+                }
+            }
+        }
+#endif
+
         [Test]
         public void TestConnectingSocketBadUserName()
         {

No bundle (reason: revision is a merge (you can force generation of a bundle with env var BZR_FORCE_BUNDLE=1)).
Thread
bzr commit into connector-net-trunk branch (reggie.burnett:963) Reggie Burnett31 Mar