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
)