1 #if WINDOWS && UNMANAGED
5 using System.Runtime.InteropServices;
6 using System.ComponentModel;
11 /// A class to handle everything associated with SSPI authentication
13 internal class SSPIHandler : IDisposable
15 #region constants and structs
17 private const int SECBUFFER_VERSION = 0;
18 private const int SECBUFFER_TOKEN = 2;
19 private const int SEC_E_OK = 0x00000000;
20 private const int SEC_I_CONTINUE_NEEDED = 0x00090312;
21 private const int ISC_REQ_ALLOCATE_MEMORY=0x00000100;
22 private const int SECURITY_NETWORK_DREP=0x00000000;
23 private const int SECPKG_CRED_OUTBOUND=0x00000002;
25 [StructLayout(LayoutKind.Sequential)]
26 private struct SecHandle
32 [StructLayout(LayoutKind.Sequential)]
33 private struct SecBuffer
36 public int BufferType;
37 public IntPtr pvBuffer;
41 /// Simplified SecBufferDesc struct with only one SecBuffer
43 [StructLayout(LayoutKind.Sequential)]
44 private struct SecBufferDesc
48 public IntPtr pBuffer;
53 #region p/invoke methods
55 [DllImport("Secur32.dll")]
56 private extern static int AcquireCredentialsHandle(
63 IntPtr pvGetKeyArgument,
64 ref SecHandle phCredential,
65 out SecHandle ptsExpiry
68 [DllImport("secur32", CharSet=CharSet.Auto, SetLastError=true)]
69 static extern int InitializeSecurityContext(
70 ref SecHandle phCredential,
71 ref SecHandle phContext,
76 ref SecBufferDesc pInput,
78 out SecHandle phNewContext,
79 out SecBufferDesc pOutput,
80 out int pfContextAttr,
81 out SecHandle ptsExpiry);
83 [DllImport("secur32", CharSet=CharSet.Auto, SetLastError=true)]
84 static extern int InitializeSecurityContext(
85 ref SecHandle phCredential,
93 out SecHandle phNewContext,
94 out SecBufferDesc pOutput,
95 out int pfContextAttr,
96 out SecHandle ptsExpiry);
98 [DllImport("Secur32.dll")]
99 private extern static int FreeContextBuffer(
100 IntPtr pvContextBuffer
103 [DllImport("Secur32.dll")]
104 private extern static int FreeCredentialsHandle(
105 ref SecHandle phCredential
108 [DllImport("Secur32.dll")]
109 private extern static int DeleteSecurityContext(
110 ref SecHandle phContext
115 private bool disposed;
116 private string sspitarget;
117 private SecHandle sspicred;
118 private SecHandle sspictx;
119 private bool sspictx_set;
121 public SSPIHandler(string pghost, string krbsrvname)
124 throw new ArgumentNullException("pghost");
125 if (krbsrvname == null)
126 krbsrvname = String.Empty;
127 sspitarget = String.Format("{0}/{1}", krbsrvname, pghost);
130 int status = AcquireCredentialsHandle(
133 SECPKG_CRED_OUTBOUND,
141 if (status != SEC_E_OK)
143 // This will automaticcaly fill in the message of the last Win32 error
144 throw new Win32Exception();
148 public string Continue(byte[] authData)
150 if (authData == null && sspictx_set)
151 throw new InvalidOperationException("The authData parameter con only be null at the first call to continue!");
159 SecBufferDesc outbuf;
160 SecHandle newContext;
164 OutBuffer.pvBuffer = IntPtr.Zero;
165 OutBuffer.BufferType = SECBUFFER_TOKEN;
166 OutBuffer.cbBuffer = 0;
168 outbuf.ulVersion = SECBUFFER_VERSION;
169 outbuf.pBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(OutBuffer));
173 Marshal.StructureToPtr(OutBuffer, outbuf.pBuffer, false);
176 inbuf.pBuffer = IntPtr.Zero;
177 InBuffer.pvBuffer = Marshal.AllocHGlobal(authData.Length);
180 Marshal.Copy(authData, 0, InBuffer.pvBuffer, authData.Length);
181 InBuffer.cbBuffer = authData.Length;
182 InBuffer.BufferType = SECBUFFER_TOKEN;
183 inbuf.ulVersion = SECBUFFER_VERSION;
185 inbuf.pBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(InBuffer));
186 Marshal.StructureToPtr(InBuffer, inbuf.pBuffer, false);
187 status = InitializeSecurityContext(
191 ISC_REQ_ALLOCATE_MEMORY,
193 SECURITY_NETWORK_DREP,
204 if (InBuffer.pvBuffer != IntPtr.Zero)
205 Marshal.FreeHGlobal(InBuffer.pvBuffer);
206 if (inbuf.pBuffer != IntPtr.Zero)
207 Marshal.FreeHGlobal(inbuf.pBuffer);
212 status = InitializeSecurityContext(
216 ISC_REQ_ALLOCATE_MEMORY,
218 SECURITY_NETWORK_DREP,
228 if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
230 // This will automaticcaly fill in the message of the last Win32 error
231 throw new Win32Exception();
235 sspictx.dwUpper = newContext.dwUpper;
236 sspictx.dwLower = newContext.dwLower;
241 if (outbuf.cBuffers > 0)
243 if (outbuf.cBuffers != 1)
245 throw new InvalidOperationException("SSPI returned invalid number of output buffers");
247 // attention: OutBuffer is still our initially created struct but outbuf.pBuffer doesn't point to
248 // it but to the copy of it we created on the unmanaged heap and passed to InitializeSecurityContext()
249 // we have to marshal it back to see the content change
250 OutBuffer = (SecBuffer)Marshal.PtrToStructure(outbuf.pBuffer, typeof(SecBuffer));
251 if (OutBuffer.cbBuffer > 0)
253 // we need the buffer with a terminating 0 so we
254 // make it one byte bigger
255 byte[] buffer = new byte[OutBuffer.cbBuffer];
256 Marshal.Copy(OutBuffer.pvBuffer, buffer, 0, buffer.Length);
257 // The SSPI authentication data must be sent as password message
259 return System.Text.Encoding.ASCII.GetString(buffer);
260 //stream.WriteByte((byte)'p');
261 //PGUtil.WriteInt32(stream, buffer.Length + 5);
262 //stream.Write(buffer, 0, buffer.Length);
270 if (OutBuffer.pvBuffer != IntPtr.Zero)
271 FreeContextBuffer(OutBuffer.pvBuffer);
272 if (outbuf.pBuffer != IntPtr.Zero)
273 Marshal.FreeHGlobal(outbuf.pBuffer);
278 #region resource cleanup
280 private void FreeHandles()
284 FreeCredentialsHandle(ref sspicred);
285 DeleteSecurityContext(ref sspictx);
294 public void Dispose()
297 GC.SuppressFinalize(this);
300 protected virtual void Dispose(bool disposing)