1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.ComponentModel;
6 using System.Diagnostics;
7 using System.Globalization;
8 using System.Net.Security;
9 using System.Runtime.InteropServices;
10 
11 namespace System.Net
12 {
13     internal static class SSPIWrapper
14     {
EnumerateSecurityPackages(SSPIInterface secModule)15         internal static SecurityPackageInfoClass[] EnumerateSecurityPackages(SSPIInterface secModule)
16         {
17             if (NetEventSource.IsEnabled) NetEventSource.Enter(null);
18 
19             if (secModule.SecurityPackages == null)
20             {
21                 lock (secModule)
22                 {
23                     if (secModule.SecurityPackages == null)
24                     {
25                         int moduleCount = 0;
26                         SafeFreeContextBuffer arrayBaseHandle = null;
27                         try
28                         {
29                             int errorCode = secModule.EnumerateSecurityPackages(out moduleCount, out arrayBaseHandle);
30                             if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"arrayBase: {arrayBaseHandle}");
31                             if (errorCode != 0)
32                             {
33                                 throw new Win32Exception(errorCode);
34                             }
35 
36                             var securityPackages = new SecurityPackageInfoClass[moduleCount];
37 
38                             int i;
39                             for (i = 0; i < moduleCount; i++)
40                             {
41                                 securityPackages[i] = new SecurityPackageInfoClass(arrayBaseHandle, i);
42                                 if (NetEventSource.IsEnabled) NetEventSource.Log.EnumerateSecurityPackages(securityPackages[i].Name);
43                             }
44 
45                             secModule.SecurityPackages = securityPackages;
46                         }
47                         finally
48                         {
49                             if (arrayBaseHandle != null)
50                             {
51                                 arrayBaseHandle.Dispose();
52                             }
53                         }
54                     }
55                 }
56             }
57 
58             if (NetEventSource.IsEnabled) NetEventSource.Exit(null);
59             return secModule.SecurityPackages;
60         }
61 
GetVerifyPackageInfo(SSPIInterface secModule, string packageName)62         internal static SecurityPackageInfoClass GetVerifyPackageInfo(SSPIInterface secModule, string packageName)
63         {
64             return GetVerifyPackageInfo(secModule, packageName, false);
65         }
66 
GetVerifyPackageInfo(SSPIInterface secModule, string packageName, bool throwIfMissing)67         internal static SecurityPackageInfoClass GetVerifyPackageInfo(SSPIInterface secModule, string packageName, bool throwIfMissing)
68         {
69             SecurityPackageInfoClass[] supportedSecurityPackages = EnumerateSecurityPackages(secModule);
70             if (supportedSecurityPackages != null)
71             {
72                 for (int i = 0; i < supportedSecurityPackages.Length; i++)
73                 {
74                     if (string.Compare(supportedSecurityPackages[i].Name, packageName, StringComparison.OrdinalIgnoreCase) == 0)
75                     {
76                         return supportedSecurityPackages[i];
77                     }
78                 }
79             }
80 
81             if (NetEventSource.IsEnabled) NetEventSource.Log.SspiPackageNotFound(packageName);
82 
83             if (throwIfMissing)
84             {
85                 throw new NotSupportedException(SR.net_securitypackagesupport);
86             }
87 
88             return null;
89         }
90 
AcquireDefaultCredential(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent)91         public static SafeFreeCredentials AcquireDefaultCredential(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent)
92         {
93             if (NetEventSource.IsEnabled)
94             {
95                 NetEventSource.Enter(null, package);
96                 NetEventSource.Log.AcquireDefaultCredential(package, intent);
97             }
98 
99             SafeFreeCredentials outCredential = null;
100             int errorCode = secModule.AcquireDefaultCredential(package, intent, out outCredential);
101 
102             if (errorCode != 0)
103             {
104                 if (NetEventSource.IsEnabled) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(AcquireDefaultCredential), $"0x{errorCode:X}"));
105                 throw new Win32Exception(errorCode);
106             }
107             return outCredential;
108         }
109 
AcquireCredentialsHandle(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, ref Interop.SspiCli.SEC_WINNT_AUTH_IDENTITY_W authdata)110         public static SafeFreeCredentials AcquireCredentialsHandle(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, ref Interop.SspiCli.SEC_WINNT_AUTH_IDENTITY_W authdata)
111         {
112             if (NetEventSource.IsEnabled)
113             {
114                 NetEventSource.Enter(null, package);
115                 NetEventSource.Log.AcquireCredentialsHandle(package, intent, authdata);
116             }
117 
118             SafeFreeCredentials credentialsHandle = null;
119             int errorCode = secModule.AcquireCredentialsHandle(package,
120                                                                intent,
121                                                                ref authdata,
122                                                                out credentialsHandle);
123 
124             if (errorCode != 0)
125             {
126                 if (NetEventSource.IsEnabled) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}"));
127 
128                 throw new Win32Exception(errorCode);
129             }
130             return credentialsHandle;
131         }
132 
AcquireCredentialsHandle(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, ref SafeSspiAuthDataHandle authdata)133         public static SafeFreeCredentials AcquireCredentialsHandle(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, ref SafeSspiAuthDataHandle authdata)
134         {
135             if (NetEventSource.IsEnabled) NetEventSource.Log.AcquireCredentialsHandle(package, intent, authdata);
136 
137             SafeFreeCredentials credentialsHandle = null;
138             int errorCode = secModule.AcquireCredentialsHandle(package, intent, ref authdata, out credentialsHandle);
139 
140             if (errorCode != 0)
141             {
142                 if (NetEventSource.IsEnabled) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}"));
143                 throw new Win32Exception(errorCode);
144             }
145 
146             return credentialsHandle;
147         }
148 
AcquireCredentialsHandle(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, Interop.SspiCli.SCHANNEL_CRED scc)149         public static SafeFreeCredentials AcquireCredentialsHandle(SSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, Interop.SspiCli.SCHANNEL_CRED scc)
150         {
151             if (NetEventSource.IsEnabled)
152             {
153                 NetEventSource.Enter(null, package);
154                 NetEventSource.Log.AcquireCredentialsHandle(package, intent, scc);
155             }
156 
157             SafeFreeCredentials outCredential = null;
158             int errorCode = secModule.AcquireCredentialsHandle(
159                                             package,
160                                             intent,
161                                             ref scc,
162                                             out outCredential);
163 
164             if (errorCode != 0)
165             {
166                 if (NetEventSource.IsEnabled) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}"));
167                 throw new Win32Exception(errorCode);
168             }
169 
170             if (NetEventSource.IsEnabled) NetEventSource.Exit(null, outCredential);
171             return outCredential;
172         }
173 
InitializeSecurityContext(SSPIInterface secModule, ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)174         internal static int InitializeSecurityContext(SSPIInterface secModule, ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)
175         {
176             if (NetEventSource.IsEnabled) NetEventSource.Log.InitializeSecurityContext(credential, context, targetName, inFlags);
177 
178             int errorCode = secModule.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, datarep, inputBuffer, outputBuffer, ref outFlags);
179 
180             if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffer(nameof(InitializeSecurityContext), inputBuffer?.size ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode);
181 
182             return errorCode;
183         }
184 
InitializeSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)185         internal static int InitializeSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)
186         {
187             if (NetEventSource.IsEnabled) NetEventSource.Log.InitializeSecurityContext(credential, context, targetName, inFlags);
188 
189             int errorCode = secModule.InitializeSecurityContext(credential, ref context, targetName, inFlags, datarep, inputBuffers, outputBuffer, ref outFlags);
190 
191             if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffers(nameof(InitializeSecurityContext), inputBuffers?.Length ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode);
192 
193             return errorCode;
194         }
195 
AcceptSecurityContext(SSPIInterface secModule, ref SafeFreeCredentials credential, ref SafeDeleteContext context, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)196         internal static int AcceptSecurityContext(SSPIInterface secModule, ref SafeFreeCredentials credential, ref SafeDeleteContext context, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)
197         {
198             if (NetEventSource.IsEnabled) NetEventSource.Log.AcceptSecurityContext(credential, context, inFlags);
199 
200             int errorCode = secModule.AcceptSecurityContext(ref credential, ref context, inputBuffer, inFlags, datarep, outputBuffer, ref outFlags);
201 
202             if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffer(nameof(AcceptSecurityContext), inputBuffer?.size ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode);
203 
204             return errorCode;
205         }
206 
AcceptSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)207         internal static int AcceptSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)
208         {
209             if (NetEventSource.IsEnabled) NetEventSource.Log.AcceptSecurityContext(credential, context, inFlags);
210 
211             int errorCode = secModule.AcceptSecurityContext(credential, ref context, inputBuffers, inFlags, datarep, outputBuffer, ref outFlags);
212 
213             if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffers(nameof(AcceptSecurityContext), inputBuffers?.Length ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode);
214 
215             return errorCode;
216         }
217 
CompleteAuthToken(SSPIInterface secModule, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers)218         internal static int CompleteAuthToken(SSPIInterface secModule, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers)
219         {
220             int errorCode = secModule.CompleteAuthToken(ref context, inputBuffers);
221 
222             if (NetEventSource.IsEnabled) NetEventSource.Log.OperationReturnedSomething(nameof(CompleteAuthToken), (Interop.SECURITY_STATUS)errorCode);
223 
224             return errorCode;
225         }
226 
ApplyControlToken(SSPIInterface secModule, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers)227         internal static int ApplyControlToken(SSPIInterface secModule, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers)
228         {
229             int errorCode = secModule.ApplyControlToken(ref context, inputBuffers);
230 
231             if (NetEventSource.IsEnabled) NetEventSource.Log.OperationReturnedSomething(nameof(ApplyControlToken), (Interop.SECURITY_STATUS)errorCode);
232 
233             return errorCode;
234         }
235 
QuerySecurityContextToken(SSPIInterface secModule, SafeDeleteContext context, out SecurityContextTokenHandle token)236         public static int QuerySecurityContextToken(SSPIInterface secModule, SafeDeleteContext context, out SecurityContextTokenHandle token)
237         {
238             return secModule.QuerySecurityContextToken(context, out token);
239         }
240 
EncryptMessage(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)241         public static int EncryptMessage(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)
242         {
243             return EncryptDecryptHelper(OP.Encrypt, secModule, context, input, sequenceNumber);
244         }
245 
DecryptMessage(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)246         public static int DecryptMessage(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)
247         {
248             return EncryptDecryptHelper(OP.Decrypt, secModule, context, input, sequenceNumber);
249         }
250 
MakeSignature(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)251         internal static int MakeSignature(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)
252         {
253             return EncryptDecryptHelper(OP.MakeSignature, secModule, context, input, sequenceNumber);
254         }
255 
VerifySignature(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)256         public static int VerifySignature(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)
257         {
258             return EncryptDecryptHelper(OP.VerifySignature, secModule, context, input, sequenceNumber);
259         }
260 
261         private enum OP
262         {
263             Encrypt = 1,
264             Decrypt,
265             MakeSignature,
266             VerifySignature
267         }
268 
EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)269         private static unsafe int EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)
270         {
271             Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(input.Length);
272             var unmanagedBuffer = new Interop.SspiCli.SecBuffer[input.Length];
273 
274             fixed (Interop.SspiCli.SecBuffer* unmanagedBufferPtr = unmanagedBuffer)
275             {
276                 sdcInOut.pBuffers = unmanagedBufferPtr;
277                 GCHandle[] pinnedBuffers = new GCHandle[input.Length];
278                 byte[][] buffers = new byte[input.Length][];
279                 try
280                 {
281                     for (int i = 0; i < input.Length; i++)
282                     {
283                         SecurityBuffer iBuffer = input[i];
284                         unmanagedBuffer[i].cbBuffer = iBuffer.size;
285                         unmanagedBuffer[i].BufferType = iBuffer.type;
286                         if (iBuffer.token == null || iBuffer.token.Length == 0)
287                         {
288                             unmanagedBuffer[i].pvBuffer = IntPtr.Zero;
289                         }
290                         else
291                         {
292                             pinnedBuffers[i] = GCHandle.Alloc(iBuffer.token, GCHandleType.Pinned);
293                             unmanagedBuffer[i].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(iBuffer.token, iBuffer.offset);
294                             buffers[i] = iBuffer.token;
295                         }
296                     }
297 
298                     // The result is written in the input Buffer passed as type=BufferType.Data.
299                     int errorCode;
300                     switch (op)
301                     {
302                         case OP.Encrypt:
303                             errorCode = secModule.EncryptMessage(context, ref sdcInOut, sequenceNumber);
304                             break;
305 
306                         case OP.Decrypt:
307                             errorCode = secModule.DecryptMessage(context, ref sdcInOut, sequenceNumber);
308                             break;
309 
310                         case OP.MakeSignature:
311                             errorCode = secModule.MakeSignature(context, ref sdcInOut, sequenceNumber);
312                             break;
313 
314                         case OP.VerifySignature:
315                             errorCode = secModule.VerifySignature(context, ref sdcInOut, sequenceNumber);
316                             break;
317 
318                         default:
319                             NetEventSource.Fail(null, $"Unknown OP: {op}");
320                             throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
321                     }
322 
323                     // Marshalling back returned sizes / data.
324                     for (int i = 0; i < input.Length; i++)
325                     {
326                         SecurityBuffer iBuffer = input[i];
327                         iBuffer.size = unmanagedBuffer[i].cbBuffer;
328                         iBuffer.type = unmanagedBuffer[i].BufferType;
329 
330                         if (iBuffer.size == 0)
331                         {
332                             iBuffer.offset = 0;
333                             iBuffer.token = null;
334                         }
335                         else
336                         {
337                             checked
338                             {
339                                 // Find the buffer this is inside of.  Usually they all point inside buffer 0.
340                                 int j;
341                                 for (j = 0; j < input.Length; j++)
342                                 {
343                                     if (buffers[j] == null)
344                                     {
345                                         continue;
346                                     }
347 
348                                     byte* bufferAddress = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers[j], 0);
349                                     if ((byte*)unmanagedBuffer[i].pvBuffer >= bufferAddress &&
350                                         (byte*)unmanagedBuffer[i].pvBuffer + iBuffer.size <= bufferAddress + buffers[j].Length)
351                                     {
352                                         iBuffer.offset = (int)((byte*)unmanagedBuffer[i].pvBuffer - bufferAddress);
353                                         iBuffer.token = buffers[j];
354                                         break;
355                                     }
356                                 }
357 
358                                 if (j >= input.Length)
359                                 {
360                                     NetEventSource.Fail(null, "Output buffer out of range.");
361                                     iBuffer.size = 0;
362                                     iBuffer.offset = 0;
363                                     iBuffer.token = null;
364                                 }
365                             }
366                         }
367 
368                         // Backup validate the new sizes.
369                         if (iBuffer.offset < 0 || iBuffer.offset > (iBuffer.token == null ? 0 : iBuffer.token.Length))
370                         {
371                             NetEventSource.Fail(null, $"'offset' out of range.  [{iBuffer.offset}]");
372                         }
373 
374                         if (iBuffer.size < 0 || iBuffer.size > (iBuffer.token == null ? 0 : iBuffer.token.Length - iBuffer.offset))
375                         {
376                             NetEventSource.Fail(null, $"'size' out of range.  [{iBuffer.size}]");
377                         }
378                     }
379 
380                     if (NetEventSource.IsEnabled && errorCode != 0)
381                     {
382                         if (errorCode == Interop.SspiCli.SEC_I_RENEGOTIATE)
383                         {
384                             NetEventSource.Error(null, SR.Format(SR.event_OperationReturnedSomething, op, "SEC_I_RENEGOTIATE"));
385                         }
386                         else
387                         {
388                             NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, op, $"0x{0:X}"));
389                         }
390                     }
391 
392                     return errorCode;
393                 }
394                 finally
395                 {
396                     for (int i = 0; i < pinnedBuffers.Length; ++i)
397                     {
398                         if (pinnedBuffers[i].IsAllocated)
399                         {
400                             pinnedBuffers[i].Free();
401                         }
402                     }
403                 }
404             }
405         }
406 
QueryContextChannelBinding(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute)407         public static SafeFreeContextBufferChannelBinding QueryContextChannelBinding(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute)
408         {
409             if (NetEventSource.IsEnabled) NetEventSource.Enter(null, contextAttribute);
410 
411             SafeFreeContextBufferChannelBinding result;
412             int errorCode = secModule.QueryContextChannelBinding(securityContext, contextAttribute, out result);
413             if (errorCode != 0)
414             {
415                 if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}");
416                 return null;
417             }
418 
419             if (NetEventSource.IsEnabled) NetEventSource.Exit(null, result);
420             return result;
421         }
422 
QueryContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute)423         public static object QueryContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute)
424         {
425             int errorCode;
426             return QueryContextAttributes(secModule, securityContext, contextAttribute, out errorCode);
427         }
428 
QueryContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute, out int errorCode)429         public static object QueryContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute, out int errorCode)
430         {
431             if (NetEventSource.IsEnabled) NetEventSource.Enter(null, contextAttribute);
432 
433             int nativeBlockSize = IntPtr.Size;
434             Type handleType = null;
435 
436             switch (contextAttribute)
437             {
438                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES:
439                     nativeBlockSize = SecPkgContext_Sizes.SizeOf;
440                     break;
441                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_STREAM_SIZES:
442                     nativeBlockSize = SecPkgContext_StreamSizes.SizeOf;
443                     break;
444 
445                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES:
446                     handleType = typeof(SafeFreeContextBuffer);
447                     break;
448 
449                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_PACKAGE_INFO:
450                     handleType = typeof(SafeFreeContextBuffer);
451                     break;
452 
453                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO:
454                     handleType = typeof(SafeFreeContextBuffer);
455                     nativeBlockSize = Marshal.SizeOf<SecPkgContext_NegotiationInfoW>();
456                     break;
457 
458                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET:
459                     handleType = typeof(SafeFreeContextBuffer);
460                     break;
461 
462                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT:
463                     handleType = typeof(SafeFreeCertContext);
464                     break;
465 
466                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_LOCAL_CERT_CONTEXT:
467                     handleType = typeof(SafeFreeCertContext);
468                     break;
469 
470                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ISSUER_LIST_EX:
471                     nativeBlockSize = Marshal.SizeOf<Interop.SspiCli.SecPkgContext_IssuerListInfoEx>();
472                     handleType = typeof(SafeFreeContextBuffer);
473                     break;
474 
475                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO:
476                     nativeBlockSize = Marshal.SizeOf<SecPkgContext_ConnectionInfo>();
477                     break;
478 
479                 case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL:
480                     nativeBlockSize = Marshal.SizeOf<Interop.SecPkgContext_ApplicationProtocol>();
481                     break;
482 
483                 default:
484                     throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(contextAttribute)), nameof(contextAttribute));
485             }
486 
487             SafeHandle sspiHandle = null;
488             object attribute = null;
489 
490             try
491             {
492                 var nativeBuffer = new byte[nativeBlockSize];
493                 errorCode = secModule.QueryContextAttributes(securityContext, contextAttribute, nativeBuffer, handleType, out sspiHandle);
494                 if (errorCode != 0)
495                 {
496                     if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}");
497                     return null;
498                 }
499 
500                 switch (contextAttribute)
501                 {
502                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES:
503                         attribute = new SecPkgContext_Sizes(nativeBuffer);
504                         break;
505 
506                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_STREAM_SIZES:
507                         attribute = new SecPkgContext_StreamSizes(nativeBuffer);
508                         break;
509 
510                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES:
511                         attribute = Marshal.PtrToStringUni(sspiHandle.DangerousGetHandle());
512                         break;
513 
514                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_PACKAGE_INFO:
515                         attribute = new SecurityPackageInfoClass(sspiHandle, 0);
516                         break;
517 
518                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO:
519                         unsafe
520                         {
521                             fixed (void* ptr = &nativeBuffer[0])
522                             {
523                                 attribute = new NegotiationInfoClass(sspiHandle, Marshal.ReadInt32(new IntPtr(ptr), SecPkgContext_NegotiationInfoW.NegotiationStateOffest));
524                             }
525                         }
526                         break;
527 
528                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET:
529                         attribute = Marshal.PtrToStringUni(sspiHandle.DangerousGetHandle());
530                         break;
531 
532                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_LOCAL_CERT_CONTEXT:
533                     // Fall-through to RemoteCertificate is intentional.
534                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT:
535                         attribute = sspiHandle;
536                         sspiHandle = null;
537                         break;
538 
539                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ISSUER_LIST_EX:
540                         attribute = new Interop.SspiCli.SecPkgContext_IssuerListInfoEx(sspiHandle, nativeBuffer);
541                         sspiHandle = null;
542                         break;
543 
544                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO:
545                         attribute = new SecPkgContext_ConnectionInfo(nativeBuffer);
546                         break;
547 
548                     case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL:
549                         unsafe
550                         {
551                             fixed (void *ptr = nativeBuffer)
552                             {
553                                 attribute = Marshal.PtrToStructure<Interop.SecPkgContext_ApplicationProtocol>(new IntPtr(ptr));
554                             }
555                         }
556                         break;
557 
558                     default:
559                         // Will return null.
560                         break;
561                 }
562             }
563             finally
564             {
565                 if (sspiHandle != null)
566                 {
567                     sspiHandle.Dispose();
568                 }
569             }
570 
571             if (NetEventSource.IsEnabled) NetEventSource.Exit(null, attribute);
572             return attribute;
573         }
574 
ErrorDescription(int errorCode)575         public static string ErrorDescription(int errorCode)
576         {
577             if (errorCode == -1)
578             {
579                 return "An exception when invoking Win32 API";
580             }
581 
582             switch ((Interop.SECURITY_STATUS)errorCode)
583             {
584                 case Interop.SECURITY_STATUS.InvalidHandle:
585                     return "Invalid handle";
586                 case Interop.SECURITY_STATUS.InvalidToken:
587                     return "Invalid token";
588                 case Interop.SECURITY_STATUS.ContinueNeeded:
589                     return "Continue needed";
590                 case Interop.SECURITY_STATUS.IncompleteMessage:
591                     return "Message incomplete";
592                 case Interop.SECURITY_STATUS.WrongPrincipal:
593                     return "Wrong principal";
594                 case Interop.SECURITY_STATUS.TargetUnknown:
595                     return "Target unknown";
596                 case Interop.SECURITY_STATUS.PackageNotFound:
597                     return "Package not found";
598                 case Interop.SECURITY_STATUS.BufferNotEnough:
599                     return "Buffer not enough";
600                 case Interop.SECURITY_STATUS.MessageAltered:
601                     return "Message altered";
602                 case Interop.SECURITY_STATUS.UntrustedRoot:
603                     return "Untrusted root";
604                 default:
605                     return "0x" + errorCode.ToString("x", NumberFormatInfo.InvariantInfo);
606             }
607         }
608     } // class SSPIWrapper
609 }
610