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

Reply via email to