Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Received "InitializeSecurityContext" failed on Windows 7 64-bit when using integrated security [DNET369] #374

Closed
firebird-automations opened this issue Mar 10, 2011 · 4 comments

Comments

@firebird-automations
Copy link

Submitted by: Nathan Fox (nathanfox)

Attachments:
SSPIHelper.cs

Votes: 1

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 http://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 http://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 (mailto:jiri@cincura.net)
* All Rights Reserved.
*
* Adapted from http://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"\);
	\}

	#&#x2060;endregion

	#&#x2060;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\(\);
		\}
	\}

	#&#x2060;endregion

	#&#x2060;region Finalizer

	\~SSPIHelper\(\)
	\{
		this\.Dispose\(false\);
	\}

	#&#x2060;endregion

	#&#x2060;region IDisposable Members

	public void Dispose\(\)
	\{
		this\.Dispose\(true\);
		GC\.SuppressFinalize\(this\);
	\}

	#&#x2060;endregion

	#&#x2060;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\);
		\}
	\}

	#&#x2060;endregion
\}

}

#⁠endif

Commits: 95be05f

@firebird-automations
Copy link
Author

Commented by: Nathan Fox (nathanfox)

Updated SSPIHelper.cs file to fix the 64-bit SSPI issue.

@firebird-automations
Copy link
Author

Modified by: Nathan Fox (nathanfox)

Attachment: SSPIHelper.cs [ 11914 ]

@firebird-automations
Copy link
Author

Modified by: @cincuranet

status: Open [ 1 ] => In Progress [ 3 ]

@firebird-automations
Copy link
Author

Modified by: @cincuranet

status: In Progress [ 3 ] => Resolved [ 5 ]

resolution: Fixed [ 1 ]

Fix Version: vNext [ 10571 ]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants