Received "InitializeSecurityContext" failed on Windows 7 64-bit when using integrated security ----------------------------------------------------------------------------------------------
Key: DNET-369 URL: http://tracker.firebirdsql.org/browse/DNET-369 Project: .NET Data provider Issue Type: Bug Components: ADO.NET Provider Affects Versions: 2.6 Environment: OS: Windows 7 64-bit. Running an ASP.NET MVC application under IIS 7.5 connecting to a Firebird 2.5 database using the Firebird .NET provider 2.6 . Reporter: Nathan Fox Assignee: Jiri Cincura Priority: Minor Attachments: SSPIHelper.cs When connecting to a Firebird database with Integrated Security=SSPI in the connection string, an exception is thrown with the message "InitializeSecurityContext failed". If I target my application for 32-bit it works, it just fails when run as a 64-bit application. I fixed the issue by updating the calls to the imported SSPI functions in FirebirdSql\Data\Client\Managed\Version11\SSPIHelper.cs file to use parameter types for the AcquireCredentialsHandle and InitializeSecurityContext functions as defined on the website pinvoke.net. I've included the changes I made to the code as a reference. It might be useful or not. I modified the SSPIHelper.cs file as follows: /* * Firebird ADO.NET Data provider for .NET and Mono * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.firebirdsql.org/index.php?op=doc&id=idpl * * Software distributed under the License is distributed on * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either * express or implied. See the License for the specific * language governing rights and limitations under the License. * * Copyright (c) 2008 Vladimir Bodecek, Jiri Cincura (j...@cincura.net) * All Rights Reserved. * * Adapted from pinvoke.net. */ #if (!LINUX) //SSPI is available only on Windows using System; using System.Text; using System.Collections; using System.Runtime.InteropServices; namespace FirebirdSql.Data.Client.Managed.Version11 { internal sealed class SSPIHelper : IDisposable { private enum SecBufferType { SECBUFFER_VERSION = 0, SECBUFFER_EMPTY = 0, SECBUFFER_DATA = 1, SECBUFFER_TOKEN = 2 } #region Structures used in native Win API calls [StructLayout(LayoutKind.Sequential)] public struct SECURITY_HANDLE { public IntPtr LowPart; public IntPtr HighPart; public SECURITY_HANDLE(int dummy) { LowPart = HighPart = 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)] private struct SecBuffer : IDisposable { private int cbBuffer; private int bufferType; private 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; this.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; } } public byte[] GetBytes() { byte[] buffer = null; if (cbBuffer > 0) { buffer = new byte[cbBuffer]; Marshal.Copy(pvBuffer, buffer, 0, cbBuffer); } return buffer; } } [StructLayout(LayoutKind.Sequential)] private 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 secBuffer = new SecBuffer(secBufferBytes); pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer)); Marshal.StructureToPtr(secBuffer, pBuffers, false); } public void Dispose() { if (pBuffers != IntPtr.Zero) { SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); secBuffer.Dispose(); Marshal.FreeHGlobal(pBuffers); pBuffers = IntPtr.Zero; } } public byte[] GetSecBufferBytes() { if (pBuffers == IntPtr.Zero) throw new ObjectDisposedException("SecBufferDesc"); SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); return secBuffer.GetBytes(); } } #endregion #region Constants used in native Win API calls const int TOKEN_QUERY = 0x00008; const int SEC_E_OK = 0; const int SEC_I_CONTINUE_NEEDED = 0x90312; const int SECPKG_CRED_INBOUND = 1; const int SECPKG_CRED_OUTBOUND = 2; const int SECURITY_NATIVE_DREP = 0x10; const int MAX_TOKEN_SIZE = 12288; const int ISC_REQ_DELEGATE = 0x00000001; const int ISC_REQ_MUTUAL_AUTH = 0x00000002; const int ISC_REQ_REPLAY_DETECT = 0x00000004; const int ISC_REQ_SEQUENCE_DETECT = 0x00000008; const int ISC_REQ_CONFIDENTIALITY = 0x00000010; const int ISC_REQ_USE_SESSION_KEY = 0x00000020; const int ISC_REQ_PROMPT_FOR_CREDS = 0x00000040; const int ISC_REQ_USE_SUPPLIED_CREDS = 0x00000080; const int ISC_REQ_ALLOCATE_MEMORY = 0x00000100; const int ISC_REQ_USE_DCE_STYLE = 0x00000200; const int ISC_REQ_DATAGRAM = 0x00000400; const int ISC_REQ_CONNECTION = 0x00000800; const int ISC_REQ_CALL_LEVEL = 0x00001000; const int ISC_REQ_FRAGMENT_SUPPLIED = 0x00002000; const int ISC_REQ_EXTENDED_ERROR = 0x00004000; const int ISC_REQ_STREAM = 0x00008000; const int ISC_REQ_INTEGRITY = 0x00010000; const int ISC_REQ_IDENTIFY = 0x00020000; const int ISC_REQ_NULL_SESSION = 0x00040000; const int ISC_REQ_MANUAL_CRED_VALIDATION = 0x00080000; const int ISC_REQ_RESERVED1 = 0x00100000; const int ISC_REQ_FRAGMENT_TO_FIT = 0x00200000; const int SECPKG_ATTR_SIZES = 0; const int STANDARD_CONTEXT_ATTRIBUTES = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONNECTION; const ulong INVALID_SEC_HANDLE = 0xFFFFFFFFFFFFFFFF; #endregion #region Prototypes of native Win API functions [DllImport("secur32", CharSet = CharSet.Auto)] static extern int AcquireCredentialsHandle( string pszPrincipal, //SEC_CHAR* string pszPackage, //SEC_CHAR* //"Kerberos","NTLM","Negotiative" int fCredentialUse, IntPtr PAuthenticationID,//_LUID AuthenticationID,//pvLogonID, //PLUID IntPtr pAuthData,//PVOID int pGetKeyFn, //SEC_GET_KEY_FN IntPtr pvGetKeyArgument, //PVOID out SECURITY_HANDLE phCredential, //SecHandle //PCtxtHandle ref out SECURITY_INTEGER ptsExpiry //PTimeStamp //TimeStamp ref ); [DllImport("secur32", CharSet = CharSet.Auto, SetLastError = true)] static extern int InitializeSecurityContext( ref SECURITY_HANDLE phCredential,//PCredHandle IntPtr phContext, //PCtxtHandle string pszTargetName, int fContextReq, int Reserved1, int TargetDataRep, IntPtr pInput, //PSecBufferDesc SecBufferDesc int Reserved2, out SECURITY_HANDLE phNewContext, //PCtxtHandle ref SecBufferDesc pOutput, //PSecBufferDesc SecBufferDesc out uint pfContextAttr, //managed ulong == 64 bits!!! out SECURITY_INTEGER ptsExpiry //PTimeStamp ); // 2 signatures of this API function needed because different usage [DllImport("secur32", CharSet = CharSet.Auto, SetLastError = true)] static extern int InitializeSecurityContext( ref SECURITY_HANDLE phCredential,//PCredHandle ref SECURITY_HANDLE phContext, //PCtxtHandle string pszTargetName, int fContextReq, int Reserved1, int TargetDataRep, ref SecBufferDesc SecBufferDesc, //PSecBufferDesc SecBufferDesc int Reserved2, out SECURITY_HANDLE phNewContext, //PCtxtHandle ref SecBufferDesc pOutput, //PSecBufferDesc SecBufferDesc out uint pfContextAttr, //managed ulong == 64 bits!!! out SECURITY_INTEGER ptsExpiry //PTimeStamp ); [DllImport("secur32")] static extern int FreeCredentialsHandle(ref SECURITY_HANDLE phCredential); //PCredHandle [DllImport("secur32")] static extern int DeleteSecurityContext(ref SECURITY_HANDLE phContext); //PCtxtHandle #endregion #region Private members private SECURITY_HANDLE clientCredentials = new SECURITY_HANDLE(0); private SECURITY_HANDLE clientContext = new SECURITY_HANDLE(0); private bool disposed = false; private string securPackage; private string remotePrincipal; #endregion #region Constructors /// <summary> /// Creates SSPIHelper with default "NTLM" security package and no remote principal and gets client credentials /// </summary> public SSPIHelper () : this("NTLM") { } /// <summary> /// Creates SSPIHelper with given security package and no remote principal and gets client credentials /// </summary> /// <param name="securPackage">Name of security package (e.g. NTLM, Kerberos, ...)</param> public SSPIHelper(string securPackage) : this(securPackage, null) { } /// <summary> /// Creates SSPIHelper with given security package and remote principal and gets client credentials /// </summary> /// <param name="securPackage">Name of security package (e.g. NTLM, Kerberos, ...)</param> /// <param name="remotePrincipal">SPN of server (may be necessary for Kerberos</param> public SSPIHelper(string securPackage, string remotePrincipal) { this.securPackage = securPackage; this.remotePrincipal = remotePrincipal; SECURITY_INTEGER expiry = new SECURITY_INTEGER(0); if (AcquireCredentialsHandle(null, securPackage, SECPKG_CRED_OUTBOUND, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, out clientCredentials, out expiry) != SEC_E_OK) throw new Exception("Acquiring client credentials failed"); } #endregion #region Methods /// <summary> /// Creates client security context and returns "client token" /// </summary> /// <returns>Client authentication data to be sent to server</returns> public byte[] InitializeClientSecurity() { if (disposed) throw new ObjectDisposedException("SSPIHelper"); CloseClientContext(); SECURITY_INTEGER expiry = new SECURITY_INTEGER(0); uint contextAttributes; SecBufferDesc clientTokenBuf = new SecBufferDesc(MAX_TOKEN_SIZE); try { int resCode = InitializeSecurityContext( ref clientCredentials, IntPtr.Zero, remotePrincipal,// null string pszTargetName, STANDARD_CONTEXT_ATTRIBUTES, 0,//int Reserved1, SECURITY_NATIVE_DREP,//int TargetDataRep IntPtr.Zero, //Always zero first time around... 0, //int Reserved2, out clientContext, //pHandle CtxtHandle = SecHandle ref clientTokenBuf,//ref SecBufferDesc pOutput, //PSecBufferDesc out contextAttributes,//ref int pfContextAttr, out expiry); //ref IntPtr ptsExpiry ); //PTimeStamp if (resCode != SEC_E_OK && resCode != SEC_I_CONTINUE_NEEDED) throw new Exception("InitializeSecurityContext failed"); return clientTokenBuf.GetSecBufferBytes(); } finally { clientTokenBuf.Dispose(); } } /// <summary> /// Creates client authentication data based on already existing security context and /// authentication data sent by server /// This method must not be called before InitializeClientSecurity /// </summary> /// <param name="serverToken">Authentication data received from server</param> /// <returns>Client authentication data to be sent to server</returns> public byte[] GetClientSecurity(byte[] serverToken) { if (disposed) throw new ObjectDisposedException("SSPIHelper"); if (clientContext.HighPart == IntPtr.Zero && clientContext.LowPart == IntPtr.Zero) throw new InvalidOperationException("InitializeClientSecurity not called"); SECURITY_INTEGER expiry = new SECURITY_INTEGER(0); uint contextAttributes; SecBufferDesc clientTokenBuf = new SecBufferDesc(MAX_TOKEN_SIZE); try { SecBufferDesc serverTokenBuf = new SecBufferDesc(serverToken); try { int resCode = InitializeSecurityContext( ref clientCredentials, ref clientContext, remotePrincipal,// null string pszTargetName, STANDARD_CONTEXT_ATTRIBUTES, 0,//int Reserved1, SECURITY_NATIVE_DREP,//int TargetDataRep ref serverTokenBuf, // server token must be ref because it is struct 0, //int Reserved2, out clientContext, //pHandle CtxtHandle = SecHandle ref clientTokenBuf,//ref SecBufferDesc pOutput, //PSecBufferDesc out contextAttributes,//ref int pfContextAttr, out expiry); //ref IntPtr ptsExpiry ); //PTimeStamp if (resCode != SEC_E_OK && resCode != SEC_I_CONTINUE_NEEDED) throw new Exception("InitializeSecurityContext() failed"); return clientTokenBuf.GetSecBufferBytes(); } finally { serverTokenBuf.Dispose(); } } finally { clientTokenBuf.Dispose(); } } #endregion #region Finalizer ~SSPIHelper() { this.Dispose(false); } #endregion #region IDisposable Members public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } #endregion #region Private methods private void Dispose(bool disposing) { lock (this) { if (!this.disposed) { if (disposing) { } CloseClientContext(); CloseClientCredentials(); this.disposed = true; } } } private void CloseClientContext() { if (!(clientContext.HighPart == IntPtr.Zero && clientContext.LowPart == IntPtr.Zero)) { DeleteSecurityContext(ref clientContext); clientContext = new SECURITY_HANDLE(0); } } private void CloseClientCredentials() { if (!(clientCredentials.HighPart == IntPtr.Zero && clientCredentials.LowPart == IntPtr.Zero)) { FreeCredentialsHandle(ref clientCredentials); clientCredentials = new SECURITY_HANDLE(0); } } #endregion } } #endif -- This message is automatically generated by JIRA. - If you think it was sent incorrectly contact one of the administrators: http://tracker.firebirdsql.org/secure/Administrators.jspa - For more information on JIRA, see: http://www.atlassian.com/software/jira ------------------------------------------------------------------------------ Colocation vs. Managed Hosting A question and answer guide to determining the best fit for your organization - today and in the future. http://p.sf.net/sfu/internap-sfd2d _______________________________________________ Firebird-net-provider mailing list Firebird-net-provider@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/firebird-net-provider