You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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"\);
\}
#⁠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
\}
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
{
}
#endif
Commits: 95be05f
The text was updated successfully, but these errors were encountered: