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.Diagnostics;
6 using System.IO;
7 using System.Security;
8 using System.Security.Principal;
9 using System.Threading;
10 using System.ComponentModel;
11 using System.Runtime.ExceptionServices;
12 using System.Security.Authentication;
13 using System.Security.Authentication.ExtendedProtection;
14 
15 namespace System.Net.Security
16 {
17     //
18     // The class maintains the state of the authentication process and the security context.
19     // It encapsulates security context and does the real work in authentication and
20     // user data encryption
21     //
22     internal class NegoState
23     {
24         private static readonly byte[] s_emptyMessage = new byte[0]; // used in reference comparisons
25         private static readonly AsyncCallback s_readCallback = new AsyncCallback(ReadCallback);
26         private static readonly AsyncCallback s_writeCallback = new AsyncCallback(WriteCallback);
27 
28         private Stream _innerStream;
29         private bool _leaveStreamOpen;
30 
31         private Exception _exception;
32 
33         private StreamFramer _framer;
34         private NTAuthentication _context;
35 
36         private int _nestedAuth;
37 
38         internal const int ERROR_TRUST_FAILURE = 1790;   // Used to serialize protectionLevel or impersonationLevel mismatch error to the remote side.
39         internal const int MaxReadFrameSize = 64 * 1024;
40         internal const int MaxWriteDataSize = 63 * 1024; // 1k for the framing and trailer that is always less as per SSPI.
41 
42         private bool _canRetryAuthentication;
43         private ProtectionLevel _expectedProtectionLevel;
44         private TokenImpersonationLevel _expectedImpersonationLevel;
45         private uint _writeSequenceNumber;
46         private uint _readSequenceNumber;
47 
48         private ExtendedProtectionPolicy _extendedProtectionPolicy;
49 
50         // SSPI does not send a server ack on successful auth.
51         // This is a state variable used to gracefully handle auth confirmation.
52         private bool _remoteOk = false;
53 
NegoState(Stream innerStream, bool leaveStreamOpen)54         internal NegoState(Stream innerStream, bool leaveStreamOpen)
55         {
56             if (innerStream == null)
57             {
58                 throw new ArgumentNullException("stream");
59             }
60 
61             _innerStream = innerStream;
62             _leaveStreamOpen = leaveStreamOpen;
63         }
64 
65         internal static string DefaultPackage
66         {
67             get
68             {
69                 return NegotiationInfoClass.Negotiate;
70             }
71         }
72 
GetIdentity()73         internal IIdentity GetIdentity()
74         {
75             CheckThrow(true);
76             return NegotiateStreamPal.GetIdentity(_context);
77         }
78 
ValidateCreateContext( string package, NetworkCredential credential, string servicePrincipalName, ExtendedProtectionPolicy policy, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel)79         internal void ValidateCreateContext(
80             string package,
81             NetworkCredential credential,
82             string servicePrincipalName,
83             ExtendedProtectionPolicy policy,
84             ProtectionLevel protectionLevel,
85             TokenImpersonationLevel impersonationLevel)
86         {
87             if (policy != null)
88             {
89                 // One of these must be set if EP is turned on
90                 if (policy.CustomChannelBinding == null && policy.CustomServiceNames == null)
91                 {
92                     throw new ArgumentException(SR.net_auth_must_specify_extended_protection_scheme, nameof(policy));
93                 }
94 
95                 _extendedProtectionPolicy = policy;
96             }
97             else
98             {
99                 _extendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
100             }
101 
102             ValidateCreateContext(package, true, credential, servicePrincipalName, _extendedProtectionPolicy.CustomChannelBinding, protectionLevel, impersonationLevel);
103         }
104 
ValidateCreateContext( string package, bool isServer, NetworkCredential credential, string servicePrincipalName, ChannelBinding channelBinding, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel)105         internal void ValidateCreateContext(
106             string package,
107             bool isServer,
108             NetworkCredential credential,
109             string servicePrincipalName,
110             ChannelBinding channelBinding,
111             ProtectionLevel protectionLevel,
112             TokenImpersonationLevel impersonationLevel)
113         {
114             if (_exception != null && !_canRetryAuthentication)
115             {
116                 ExceptionDispatchInfo.Throw(_exception);
117             }
118 
119             if (_context != null && _context.IsValidContext)
120             {
121                 throw new InvalidOperationException(SR.net_auth_reauth);
122             }
123 
124             if (credential == null)
125             {
126                 throw new ArgumentNullException(nameof(credential));
127             }
128 
129             if (servicePrincipalName == null)
130             {
131                 throw new ArgumentNullException(nameof(servicePrincipalName));
132             }
133 
134             NegotiateStreamPal.ValidateImpersonationLevel(impersonationLevel);
135             if (_context != null && IsServer != isServer)
136             {
137                 throw new InvalidOperationException(SR.net_auth_client_server);
138             }
139 
140             _exception = null;
141             _remoteOk = false;
142             _framer = new StreamFramer(_innerStream);
143             _framer.WriteHeader.MessageId = FrameHeader.HandshakeId;
144 
145             _expectedProtectionLevel = protectionLevel;
146             _expectedImpersonationLevel = isServer ? impersonationLevel : TokenImpersonationLevel.None;
147             _writeSequenceNumber = 0;
148             _readSequenceNumber = 0;
149 
150             ContextFlagsPal flags = ContextFlagsPal.Connection;
151 
152             // A workaround for the client when talking to Win9x on the server side.
153             if (protectionLevel == ProtectionLevel.None && !isServer)
154             {
155                 package = NegotiationInfoClass.NTLM;
156             }
157             else if (protectionLevel == ProtectionLevel.EncryptAndSign)
158             {
159                 flags |= ContextFlagsPal.Confidentiality;
160             }
161             else if (protectionLevel == ProtectionLevel.Sign)
162             {
163                 // Assuming user expects NT4 SP4 and above.
164                 flags |= (ContextFlagsPal.ReplayDetect | ContextFlagsPal.SequenceDetect | ContextFlagsPal.InitIntegrity);
165             }
166 
167             if (isServer)
168             {
169                 if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
170                 {
171                     flags |= ContextFlagsPal.AllowMissingBindings;
172                 }
173 
174                 if (_extendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never &&
175                     _extendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy)
176                 {
177                     flags |= ContextFlagsPal.ProxyBindings;
178                 }
179             }
180             else
181             {
182                 // Server side should not request any of these flags.
183                 if (protectionLevel != ProtectionLevel.None)
184                 {
185                     flags |= ContextFlagsPal.MutualAuth;
186                 }
187 
188                 if (impersonationLevel == TokenImpersonationLevel.Identification)
189                 {
190                     flags |= ContextFlagsPal.InitIdentify;
191                 }
192 
193                 if (impersonationLevel == TokenImpersonationLevel.Delegation)
194                 {
195                     flags |= ContextFlagsPal.Delegate;
196                 }
197             }
198 
199             _canRetryAuthentication = false;
200 
201             try
202             {
203                 _context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding);
204             }
205             catch (Win32Exception e)
206             {
207                 throw new AuthenticationException(SR.net_auth_SSPI, e);
208             }
209         }
210 
SetException(Exception e)211         private Exception SetException(Exception e)
212         {
213             if (_exception == null || !(_exception is ObjectDisposedException))
214             {
215                 _exception = e;
216             }
217 
218             if (_exception != null && _context != null)
219             {
220                 _context.CloseContext();
221             }
222 
223             return _exception;
224         }
225 
226         internal bool IsAuthenticated
227         {
228             get
229             {
230                 return _context != null && HandshakeComplete && _exception == null && _remoteOk;
231             }
232         }
233 
234         internal bool IsMutuallyAuthenticated
235         {
236             get
237             {
238                 if (!IsAuthenticated)
239                 {
240                     return false;
241                 }
242 
243                 // Suppressing for NTLM since SSPI does not return correct value in the context flags.
244                 if (_context.IsNTLM)
245                 {
246                     return false;
247                 }
248 
249                 return _context.IsMutualAuthFlag;
250             }
251         }
252 
253         internal bool IsEncrypted
254         {
255             get
256             {
257                 return IsAuthenticated && _context.IsConfidentialityFlag;
258             }
259         }
260 
261         internal bool IsSigned
262         {
263             get
264             {
265                 return IsAuthenticated && (_context.IsIntegrityFlag || _context.IsConfidentialityFlag);
266             }
267         }
268 
269         internal bool IsServer
270         {
271             get
272             {
273                 return _context != null && _context.IsServer;
274             }
275         }
276 
277         internal bool CanGetSecureStream
278         {
279             get
280             {
281                 return (_context.IsConfidentialityFlag || _context.IsIntegrityFlag);
282             }
283         }
284 
285         internal TokenImpersonationLevel AllowedImpersonation
286         {
287             get
288             {
289                 CheckThrow(true);
290                 return PrivateImpersonationLevel;
291             }
292         }
293 
294         private TokenImpersonationLevel PrivateImpersonationLevel
295         {
296             get
297             {
298                 // We should suppress the delegate flag in NTLM case.
299                 return (_context.IsDelegationFlag && _context.ProtocolName != NegotiationInfoClass.NTLM) ? TokenImpersonationLevel.Delegation
300                         : _context.IsIdentifyFlag ? TokenImpersonationLevel.Identification
301                         : TokenImpersonationLevel.Impersonation;
302             }
303         }
304 
305         private bool HandshakeComplete
306         {
307             get
308             {
309                 return _context.IsCompleted && _context.IsValidContext;
310             }
311         }
312 
CheckThrow(bool authSucessCheck)313         internal void CheckThrow(bool authSucessCheck)
314         {
315             if (_exception != null)
316             {
317                 ExceptionDispatchInfo.Throw(_exception);
318             }
319 
320             if (authSucessCheck && !IsAuthenticated)
321             {
322                 throw new InvalidOperationException(SR.net_auth_noauth);
323             }
324         }
325 
326         //
327         // This is to not depend on GC&SafeHandle class if the context is not needed anymore.
328         //
Close()329         internal void Close()
330         {
331             // Mark this instance as disposed.
332             _exception = new ObjectDisposedException("NegotiateStream");
333             if (_context != null)
334             {
335                 _context.CloseContext();
336             }
337         }
338 
ProcessAuthentication(LazyAsyncResult lazyResult)339         internal void ProcessAuthentication(LazyAsyncResult lazyResult)
340         {
341             CheckThrow(false);
342             if (Interlocked.Exchange(ref _nestedAuth, 1) == 1)
343             {
344                 throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, lazyResult == null ? "BeginAuthenticate" : "Authenticate", "authenticate"));
345             }
346 
347             try
348             {
349                 if (_context.IsServer)
350                 {
351                     // Listen for a client blob.
352                     StartReceiveBlob(lazyResult);
353                 }
354                 else
355                 {
356                     // Start with the first blob.
357                     StartSendBlob(null, lazyResult);
358                 }
359             }
360             catch (Exception e)
361             {
362                 // Round-trip it through SetException().
363                 e = SetException(e);
364                 throw;
365             }
366             finally
367             {
368                 if (lazyResult == null || _exception != null)
369                 {
370                     _nestedAuth = 0;
371                 }
372             }
373         }
374 
EndProcessAuthentication(IAsyncResult result)375         internal void EndProcessAuthentication(IAsyncResult result)
376         {
377             if (result == null)
378             {
379                 throw new ArgumentNullException("asyncResult");
380             }
381 
382             LazyAsyncResult lazyResult = result as LazyAsyncResult;
383             if (lazyResult == null)
384             {
385                 throw new ArgumentException(SR.Format(SR.net_io_async_result, result.GetType().FullName), "asyncResult");
386             }
387 
388             if (Interlocked.Exchange(ref _nestedAuth, 0) == 0)
389             {
390                 throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndAuthenticate"));
391             }
392 
393             // No "artificial" timeouts implemented so far, InnerStream controls that.
394             lazyResult.InternalWaitForCompletion();
395 
396             Exception e = lazyResult.Result as Exception;
397 
398             if (e != null)
399             {
400                 // Round-trip it through the SetException().
401                 e = SetException(e);
402                 ExceptionDispatchInfo.Throw(e);
403             }
404         }
405 
CheckSpn()406         private bool CheckSpn()
407         {
408             if (_context.IsKerberos)
409             {
410                 return true;
411             }
412 
413             if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Never ||
414                     _extendedProtectionPolicy.CustomServiceNames == null)
415             {
416                 return true;
417             }
418 
419             string clientSpn = _context.ClientSpecifiedSpn;
420 
421             if (String.IsNullOrEmpty(clientSpn))
422             {
423                 if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
424                 {
425                     return true;
426                 }
427             }
428             else
429             {
430                 return _extendedProtectionPolicy.CustomServiceNames.Contains(clientSpn);
431             }
432 
433             return false;
434         }
435 
436         //
437         // Client side starts here, but server also loops through this method.
438         //
StartSendBlob(byte[] message, LazyAsyncResult lazyResult)439         private void StartSendBlob(byte[] message, LazyAsyncResult lazyResult)
440         {
441             Exception exception = null;
442             if (message != s_emptyMessage)
443             {
444                 message = GetOutgoingBlob(message, ref exception);
445             }
446 
447             if (exception != null)
448             {
449                 // Signal remote side on a failed attempt.
450                 StartSendAuthResetSignal(lazyResult, message, exception);
451                 return;
452             }
453 
454             if (HandshakeComplete)
455             {
456                 if (_context.IsServer && !CheckSpn())
457                 {
458                     exception = new AuthenticationException(SR.net_auth_bad_client_creds_or_target_mismatch);
459                     int statusCode = ERROR_TRUST_FAILURE;
460                     message = new byte[8];  //sizeof(long)
461 
462                     for (int i = message.Length - 1; i >= 0; --i)
463                     {
464                         message[i] = (byte)(statusCode & 0xFF);
465                         statusCode = (int)((uint)statusCode >> 8);
466                     }
467 
468                     StartSendAuthResetSignal(lazyResult, message, exception);
469                     return;
470                 }
471 
472                 if (PrivateImpersonationLevel < _expectedImpersonationLevel)
473                 {
474                     exception = new AuthenticationException(SR.Format(SR.net_auth_context_expectation, _expectedImpersonationLevel.ToString(), PrivateImpersonationLevel.ToString()));
475                     int statusCode = ERROR_TRUST_FAILURE;
476                     message = new byte[8];  //sizeof(long)
477 
478                     for (int i = message.Length - 1; i >= 0; --i)
479                     {
480                         message[i] = (byte)(statusCode & 0xFF);
481                         statusCode = (int)((uint)statusCode >> 8);
482                     }
483 
484                     StartSendAuthResetSignal(lazyResult, message, exception);
485                     return;
486                 }
487 
488                 ProtectionLevel result = _context.IsConfidentialityFlag ? ProtectionLevel.EncryptAndSign : _context.IsIntegrityFlag ? ProtectionLevel.Sign : ProtectionLevel.None;
489 
490                 if (result < _expectedProtectionLevel)
491                 {
492                     exception = new AuthenticationException(SR.Format(SR.net_auth_context_expectation, result.ToString(), _expectedProtectionLevel.ToString()));
493                     int statusCode = ERROR_TRUST_FAILURE;
494                     message = new byte[8];  //sizeof(long)
495 
496                     for (int i = message.Length - 1; i >= 0; --i)
497                     {
498                         message[i] = (byte)(statusCode & 0xFF);
499                         statusCode = (int)((uint)statusCode >> 8);
500                     }
501 
502                     StartSendAuthResetSignal(lazyResult, message, exception);
503                     return;
504                 }
505 
506                 // Signal remote party that we are done
507                 _framer.WriteHeader.MessageId = FrameHeader.HandshakeDoneId;
508                 if (_context.IsServer)
509                 {
510                     // Server may complete now because client SSPI would not complain at this point.
511                     _remoteOk = true;
512 
513                     // However the client will wait for server to send this ACK
514                     //Force signaling server OK to the client
515                     if (message == null)
516                     {
517                         message = s_emptyMessage;
518                     }
519                 }
520             }
521             else if (message == null || message == s_emptyMessage)
522             {
523                 throw new InternalException();
524             }
525 
526             if (message != null)
527             {
528                 //even if we are completed, there could be a blob for sending.
529                 if (lazyResult == null)
530                 {
531                     _framer.WriteMessage(message);
532                 }
533                 else
534                 {
535                     IAsyncResult ar = _framer.BeginWriteMessage(message, s_writeCallback, lazyResult);
536                     if (!ar.CompletedSynchronously)
537                     {
538                         return;
539                     }
540                     _framer.EndWriteMessage(ar);
541                 }
542             }
543             CheckCompletionBeforeNextReceive(lazyResult);
544         }
545 
546         //
547         // This will check and logically complete the auth handshake.
548         //
CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult)549         private void CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult)
550         {
551             if (HandshakeComplete && _remoteOk)
552             {
553                 // We are done with success.
554                 if (lazyResult != null)
555                 {
556                     lazyResult.InvokeCallback();
557                 }
558 
559                 return;
560             }
561 
562             StartReceiveBlob(lazyResult);
563         }
564 
565         //
566         // Server side starts here, but client also loops through this method.
567         //
StartReceiveBlob(LazyAsyncResult lazyResult)568         private void StartReceiveBlob(LazyAsyncResult lazyResult)
569         {
570             byte[] message;
571             if (lazyResult == null)
572             {
573                 message = _framer.ReadMessage();
574             }
575             else
576             {
577                 IAsyncResult ar = _framer.BeginReadMessage(s_readCallback, lazyResult);
578                 if (!ar.CompletedSynchronously)
579                 {
580                     return;
581                 }
582 
583                 message = _framer.EndReadMessage(ar);
584             }
585 
586             ProcessReceivedBlob(message, lazyResult);
587         }
588 
ProcessReceivedBlob(byte[] message, LazyAsyncResult lazyResult)589         private void ProcessReceivedBlob(byte[] message, LazyAsyncResult lazyResult)
590         {
591             // This is an EOF otherwise we would get at least *empty* message but not a null one.
592             if (message == null)
593             {
594                 throw new AuthenticationException(SR.net_auth_eof, null);
595             }
596 
597             // Process Header information.
598             if (_framer.ReadHeader.MessageId == FrameHeader.HandshakeErrId)
599             {
600                 if (message.Length >= 8)    // sizeof(long)
601                 {
602                     // Try to recover remote win32 Exception.
603                     long error = 0;
604                     for (int i = 0; i < 8; ++i)
605                     {
606                         error = (error << 8) + message[i];
607                     }
608 
609                     ThrowCredentialException(error);
610                 }
611 
612                 throw new AuthenticationException(SR.net_auth_alert, null);
613             }
614 
615             if (_framer.ReadHeader.MessageId == FrameHeader.HandshakeDoneId)
616             {
617                 _remoteOk = true;
618             }
619             else if (_framer.ReadHeader.MessageId != FrameHeader.HandshakeId)
620             {
621                 throw new AuthenticationException(SR.Format(SR.net_io_header_id, "MessageId", _framer.ReadHeader.MessageId, FrameHeader.HandshakeId), null);
622             }
623 
624             CheckCompletionBeforeNextSend(message, lazyResult);
625         }
626 
627         //
628         // This will check and logically complete the auth handshake.
629         //
CheckCompletionBeforeNextSend(byte[] message, LazyAsyncResult lazyResult)630         private void CheckCompletionBeforeNextSend(byte[] message, LazyAsyncResult lazyResult)
631         {
632             //If we are done don't go into send.
633             if (HandshakeComplete)
634             {
635                 if (!_remoteOk)
636                 {
637                     throw new AuthenticationException(SR.Format(SR.net_io_header_id, "MessageId", _framer.ReadHeader.MessageId, FrameHeader.HandshakeDoneId), null);
638                 }
639                 if (lazyResult != null)
640                 {
641                     lazyResult.InvokeCallback();
642                 }
643 
644                 return;
645             }
646 
647             // Not yet done, get a new blob and send it if any.
648             StartSendBlob(message, lazyResult);
649         }
650 
651         //
652         //  This is to reset auth state on the remote side.
653         //  If this write succeeds we will allow auth retrying.
654         //
StartSendAuthResetSignal(LazyAsyncResult lazyResult, byte[] message, Exception exception)655         private void StartSendAuthResetSignal(LazyAsyncResult lazyResult, byte[] message, Exception exception)
656         {
657             _framer.WriteHeader.MessageId = FrameHeader.HandshakeErrId;
658 
659             if (IsLogonDeniedException(exception))
660             {
661                 if (IsServer)
662                 {
663                     exception = new InvalidCredentialException(SR.net_auth_bad_client_creds, exception);
664                 }
665                 else
666                 {
667                     exception = new InvalidCredentialException(SR.net_auth_bad_client_creds_or_target_mismatch, exception);
668                 }
669             }
670 
671             if (!(exception is AuthenticationException))
672             {
673                 exception = new AuthenticationException(SR.net_auth_SSPI, exception);
674             }
675 
676             if (lazyResult == null)
677             {
678                 _framer.WriteMessage(message);
679             }
680             else
681             {
682                 lazyResult.Result = exception;
683                 IAsyncResult ar = _framer.BeginWriteMessage(message, s_writeCallback, lazyResult);
684                 if (!ar.CompletedSynchronously)
685                 {
686                     return;
687                 }
688 
689                 _framer.EndWriteMessage(ar);
690             }
691 
692             _canRetryAuthentication = true;
693             ExceptionDispatchInfo.Throw(exception);
694         }
695 
WriteCallback(IAsyncResult transportResult)696         private static void WriteCallback(IAsyncResult transportResult)
697         {
698             if (!(transportResult.AsyncState is LazyAsyncResult))
699             {
700                 NetEventSource.Fail(transportResult, "State type is wrong, expected LazyAsyncResult.");
701             }
702 
703             if (transportResult.CompletedSynchronously)
704             {
705                 return;
706             }
707 
708             LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;
709 
710             // Async completion.
711             try
712             {
713                 NegoState authState = (NegoState)lazyResult.AsyncObject;
714                 authState._framer.EndWriteMessage(transportResult);
715 
716                 // Special case for an error notification.
717                 if (lazyResult.Result is Exception e)
718                 {
719                     authState._canRetryAuthentication = true;
720                     ExceptionDispatchInfo.Throw(e);
721                 }
722 
723                 authState.CheckCompletionBeforeNextReceive(lazyResult);
724             }
725             catch (Exception e)
726             {
727                 if (lazyResult.InternalPeekCompleted)
728                 {
729                     // This will throw on a worker thread.
730                     throw;
731                 }
732 
733                 lazyResult.InvokeCallback(e);
734             }
735         }
736 
ReadCallback(IAsyncResult transportResult)737         private static void ReadCallback(IAsyncResult transportResult)
738         {
739             if (!(transportResult.AsyncState is LazyAsyncResult))
740             {
741                 NetEventSource.Fail(transportResult, "State type is wrong, expected LazyAsyncResult.");
742             }
743 
744             if (transportResult.CompletedSynchronously)
745             {
746                 return;
747             }
748 
749             LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;
750 
751             // Async completion.
752             try
753             {
754                 NegoState authState = (NegoState)lazyResult.AsyncObject;
755                 byte[] message = authState._framer.EndReadMessage(transportResult);
756                 authState.ProcessReceivedBlob(message, lazyResult);
757             }
758             catch (Exception e)
759             {
760                 if (lazyResult.InternalPeekCompleted)
761                 {
762                     // This will throw on a worker thread.
763                     throw;
764                 }
765 
766                 lazyResult.InvokeCallback(e);
767             }
768         }
769 
IsError(SecurityStatusPal status)770         internal static bool IsError(SecurityStatusPal status)
771         {
772             return ((int)status.ErrorCode >= (int)SecurityStatusPalErrorCode.OutOfMemory);
773         }
774 
GetOutgoingBlob(byte[] incomingBlob, ref Exception e)775         private unsafe byte[] GetOutgoingBlob(byte[] incomingBlob, ref Exception e)
776         {
777             SecurityStatusPal statusCode;
778             byte[] message = _context.GetOutgoingBlob(incomingBlob, false, out statusCode);
779 
780             if (IsError(statusCode))
781             {
782                 e = NegotiateStreamPal.CreateExceptionFromError(statusCode);
783                 uint error = (uint)e.HResult;
784 
785                 message = new byte[sizeof(long)];
786                 for (int i = message.Length - 1; i >= 0; --i)
787                 {
788                     message[i] = (byte)(error & 0xFF);
789                     error = (error >> 8);
790                 }
791             }
792 
793             if (message != null && message.Length == 0)
794             {
795                 message = s_emptyMessage;
796             }
797 
798             return message;
799         }
800 
EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer)801         internal int EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer)
802         {
803             CheckThrow(true);
804 
805             // SSPI seems to ignore this sequence number.
806             ++_writeSequenceNumber;
807             return _context.Encrypt(buffer, offset, count, ref outBuffer, _writeSequenceNumber);
808         }
809 
DecryptData(byte[] buffer, int offset, int count, out int newOffset)810         internal int DecryptData(byte[] buffer, int offset, int count, out int newOffset)
811         {
812             CheckThrow(true);
813 
814             // SSPI seems to ignore this sequence number.
815             ++_readSequenceNumber;
816             return _context.Decrypt(buffer, offset, count, out newOffset, _readSequenceNumber);
817         }
818 
ThrowCredentialException(long error)819         internal static void ThrowCredentialException(long error)
820         {
821             Win32Exception e = new Win32Exception((int)error);
822 
823             if (e.NativeErrorCode == (int)SecurityStatusPalErrorCode.LogonDenied)
824             {
825                 throw new InvalidCredentialException(SR.net_auth_bad_client_creds, e);
826             }
827 
828             if (e.NativeErrorCode == NegoState.ERROR_TRUST_FAILURE)
829             {
830                 throw new AuthenticationException(SR.net_auth_context_expectation_remote, e);
831             }
832 
833             throw new AuthenticationException(SR.net_auth_alert, e);
834         }
835 
IsLogonDeniedException(Exception exception)836         internal static bool IsLogonDeniedException(Exception exception)
837         {
838             Win32Exception win32exception = exception as Win32Exception;
839 
840             return (win32exception != null) && (win32exception.NativeErrorCode == (int)SecurityStatusPalErrorCode.LogonDenied);
841         }
842     }
843 }
844