1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.ServiceModel.Channels
6 {
7     using System.Collections.ObjectModel;
8     using System.IdentityModel.Policy;
9     using System.IdentityModel.Selectors;
10     using System.IdentityModel.Tokens;
11     using System.IO;
12     using System.Net;
13     using System.Net.Security;
14     using System.Runtime;
15     using System.Security.Authentication;
16     using System.Security.Principal;
17     using System.ServiceModel;
18     using System.ServiceModel.Description;
19     using System.ServiceModel.Diagnostics.Application;
20     using System.ServiceModel.Security;
21 
22     class WindowsStreamSecurityUpgradeProvider : StreamSecurityUpgradeProvider
23     {
24         bool extractGroupsForWindowsAccounts;
25         EndpointIdentity identity;
26         IdentityVerifier identityVerifier;
27         ProtectionLevel protectionLevel;
28         SecurityTokenManager securityTokenManager;
29         NetworkCredential serverCredential;
30         string scheme;
31         bool isClient;
32         Uri listenUri;
33 
WindowsStreamSecurityUpgradeProvider(WindowsStreamSecurityBindingElement bindingElement, BindingContext context, bool isClient)34         public WindowsStreamSecurityUpgradeProvider(WindowsStreamSecurityBindingElement bindingElement,
35             BindingContext context, bool isClient)
36             : base(context.Binding)
37         {
38             this.extractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts;
39             this.protectionLevel = bindingElement.ProtectionLevel;
40             this.scheme = context.Binding.Scheme;
41             this.isClient = isClient;
42             this.listenUri = TransportSecurityHelpers.GetListenUri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress);
43 
44             SecurityCredentialsManager credentialProvider = context.BindingParameters.Find<SecurityCredentialsManager>();
45             if (credentialProvider == null)
46             {
47                 if (isClient)
48                 {
49                     credentialProvider = ClientCredentials.CreateDefaultCredentials();
50                 }
51                 else
52                 {
53                     credentialProvider = ServiceCredentials.CreateDefaultCredentials();
54                 }
55             }
56 
57 
58             this.securityTokenManager = credentialProvider.CreateSecurityTokenManager();
59         }
60 
61         public string Scheme
62         {
63             get { return this.scheme; }
64         }
65 
66         internal bool ExtractGroupsForWindowsAccounts
67         {
68             get
69             {
70                 return this.extractGroupsForWindowsAccounts;
71             }
72         }
73 
74         public override EndpointIdentity Identity
75         {
76             get
77             {
78                 // If the server credential is null, then we have not been opened yet and have no identity to expose.
79                 if (this.serverCredential != null)
80                 {
81                     if (this.identity == null)
82                     {
83                         lock (ThisLock)
84                         {
85                             if (this.identity == null)
86                             {
87                                 this.identity = SecurityUtils.CreateWindowsIdentity(this.serverCredential);
88                             }
89                         }
90                     }
91                 }
92                 return this.identity;
93             }
94         }
95 
96         internal IdentityVerifier IdentityVerifier
97         {
98             get
99             {
100                 return this.identityVerifier;
101             }
102         }
103 
104         public ProtectionLevel ProtectionLevel
105         {
106             get
107             {
108                 return protectionLevel;
109             }
110         }
111 
112         NetworkCredential ServerCredential
113         {
114             get
115             {
116                 return this.serverCredential;
117             }
118         }
119 
CreateUpgradeAcceptor()120         public override StreamUpgradeAcceptor CreateUpgradeAcceptor()
121         {
122             ThrowIfDisposedOrNotOpen();
123             return new WindowsStreamSecurityUpgradeAcceptor(this);
124         }
125 
CreateUpgradeInitiator(EndpointAddress remoteAddress, Uri via)126         public override StreamUpgradeInitiator CreateUpgradeInitiator(EndpointAddress remoteAddress, Uri via)
127         {
128             ThrowIfDisposedOrNotOpen();
129             return new WindowsStreamSecurityUpgradeInitiator(this, remoteAddress, via);
130         }
131 
OnAbort()132         protected override void OnAbort()
133         {
134         }
135 
OnClose(TimeSpan timeout)136         protected override void OnClose(TimeSpan timeout)
137         {
138         }
139 
OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)140         protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
141         {
142             return new CompletedAsyncResult(callback, state);
143         }
144 
OnEndClose(IAsyncResult result)145         protected override void OnEndClose(IAsyncResult result)
146         {
147             CompletedAsyncResult.End(result);
148         }
149 
OnOpen(TimeSpan timeout)150         protected override void OnOpen(TimeSpan timeout)
151         {
152             if (!isClient)
153             {
154                 SecurityTokenRequirement sspiTokenRequirement = TransportSecurityHelpers.CreateSspiTokenRequirement(this.Scheme, this.listenUri);
155                 this.serverCredential =
156                     TransportSecurityHelpers.GetSspiCredential(this.securityTokenManager, sspiTokenRequirement, timeout,
157                     out this.extractGroupsForWindowsAccounts);
158             }
159         }
160 
OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)161         protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
162         {
163             OnOpen(timeout);
164             return new CompletedAsyncResult(callback, state);
165         }
166 
OnEndOpen(IAsyncResult result)167         protected override void OnEndOpen(IAsyncResult result)
168         {
169             CompletedAsyncResult.End(result);
170         }
171 
OnOpened()172         protected override void OnOpened()
173         {
174             base.OnOpened();
175 
176             if (this.identityVerifier == null)
177             {
178                 this.identityVerifier = IdentityVerifier.CreateDefault();
179             }
180 
181             if (this.serverCredential == null)
182             {
183                 this.serverCredential = CredentialCache.DefaultNetworkCredentials;
184             }
185         }
186 
187         class WindowsStreamSecurityUpgradeAcceptor : StreamSecurityUpgradeAcceptorBase
188         {
189             WindowsStreamSecurityUpgradeProvider parent;
190             SecurityMessageProperty clientSecurity;
191 
WindowsStreamSecurityUpgradeAcceptor(WindowsStreamSecurityUpgradeProvider parent)192             public WindowsStreamSecurityUpgradeAcceptor(WindowsStreamSecurityUpgradeProvider parent)
193                 : base(FramingUpgradeString.Negotiate)
194             {
195                 this.parent = parent;
196                 this.clientSecurity = new SecurityMessageProperty();
197             }
198 
OnAcceptUpgrade(Stream stream, out SecurityMessageProperty remoteSecurity)199             protected override Stream OnAcceptUpgrade(Stream stream, out SecurityMessageProperty remoteSecurity)
200             {
201                 // wrap stream
202                 NegotiateStream negotiateStream = new NegotiateStream(stream);
203 
204                 // authenticate
205                 try
206                 {
207                     if (TD.WindowsStreamSecurityOnAcceptUpgradeIsEnabled())
208                     {
209                         TD.WindowsStreamSecurityOnAcceptUpgrade(this.EventTraceActivity);
210                     }
211 
212                     negotiateStream.AuthenticateAsServer(parent.ServerCredential, parent.ProtectionLevel,
213                         TokenImpersonationLevel.Identification);
214                 }
215                 catch (AuthenticationException exception)
216                 {
217                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(exception.Message,
218                         exception));
219                 }
220                 catch (IOException ioException)
221                 {
222                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(
223                         SR.GetString(SR.NegotiationFailedIO, ioException.Message), ioException));
224                 }
225 
226                 remoteSecurity = CreateClientSecurity(negotiateStream, parent.ExtractGroupsForWindowsAccounts);
227                 return negotiateStream;
228             }
229 
OnBeginAcceptUpgrade(Stream stream, AsyncCallback callback, object state)230             protected override IAsyncResult OnBeginAcceptUpgrade(Stream stream, AsyncCallback callback, object state)
231             {
232                 AcceptUpgradeAsyncResult result = new AcceptUpgradeAsyncResult(this, callback, state);
233                 result.Begin(stream);
234                 return result;
235             }
236 
OnEndAcceptUpgrade(IAsyncResult result, out SecurityMessageProperty remoteSecurity)237             protected override Stream OnEndAcceptUpgrade(IAsyncResult result,
238                 out SecurityMessageProperty remoteSecurity)
239             {
240                 return AcceptUpgradeAsyncResult.End(result, out remoteSecurity);
241             }
242 
CreateClientSecurity(NegotiateStream negotiateStream, bool extractGroupsForWindowsAccounts)243             SecurityMessageProperty CreateClientSecurity(NegotiateStream negotiateStream,
244                 bool extractGroupsForWindowsAccounts)
245             {
246                 WindowsIdentity remoteIdentity = (WindowsIdentity)negotiateStream.RemoteIdentity;
247                 SecurityUtils.ValidateAnonymityConstraint(remoteIdentity, false);
248                 WindowsSecurityTokenAuthenticator authenticator = new WindowsSecurityTokenAuthenticator(extractGroupsForWindowsAccounts);
249 
250                 // When NegotiateStream returns a WindowsIdentity the AuthenticationType is passed in the constructor to WindowsIdentity
251                 // by it's internal NegoState class.  If this changes, then the call to remoteIdentity.AuthenticationType could fail if the
252                 // current process token doesn't have sufficient priviledges.  It is a first class exception, and caught by the CLR
253                 // null is returned.
254                 SecurityToken token = new WindowsSecurityToken(remoteIdentity, SecurityUniqueId.Create().Value, remoteIdentity.AuthenticationType);
255                 ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = authenticator.ValidateToken(token);
256                 this.clientSecurity = new SecurityMessageProperty();
257                 this.clientSecurity.TransportToken = new SecurityTokenSpecification(token, authorizationPolicies);
258                 this.clientSecurity.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies);
259                 return this.clientSecurity;
260             }
261 
GetRemoteSecurity()262             public override SecurityMessageProperty GetRemoteSecurity()
263             {
264                 if (this.clientSecurity.TransportToken != null)
265                 {
266                     return this.clientSecurity;
267                 }
268                 return base.GetRemoteSecurity();
269             }
270 
271             class AcceptUpgradeAsyncResult : StreamSecurityUpgradeAcceptorAsyncResult
272             {
273                 WindowsStreamSecurityUpgradeAcceptor acceptor;
274                 NegotiateStream negotiateStream;
275 
AcceptUpgradeAsyncResult(WindowsStreamSecurityUpgradeAcceptor acceptor, AsyncCallback callback, object state)276                 public AcceptUpgradeAsyncResult(WindowsStreamSecurityUpgradeAcceptor acceptor, AsyncCallback callback,
277                     object state)
278                     : base(callback, state)
279                 {
280                     this.acceptor = acceptor;
281                 }
282 
OnBegin(Stream stream, AsyncCallback callback)283                 protected override IAsyncResult OnBegin(Stream stream, AsyncCallback callback)
284                 {
285                     this.negotiateStream = new NegotiateStream(stream);
286                     return this.negotiateStream.BeginAuthenticateAsServer(this.acceptor.parent.ServerCredential,
287                         this.acceptor.parent.ProtectionLevel, TokenImpersonationLevel.Identification, callback, this);
288                 }
289 
OnCompleteAuthenticateAsServer(IAsyncResult result)290                 protected override Stream OnCompleteAuthenticateAsServer(IAsyncResult result)
291                 {
292                     this.negotiateStream.EndAuthenticateAsServer(result);
293                     return this.negotiateStream;
294                 }
295 
ValidateCreateSecurity()296                 protected override SecurityMessageProperty ValidateCreateSecurity()
297                 {
298                     return this.acceptor.CreateClientSecurity(this.negotiateStream, this.acceptor.parent.ExtractGroupsForWindowsAccounts);
299                 }
300             }
301         }
302 
303         class WindowsStreamSecurityUpgradeInitiator : StreamSecurityUpgradeInitiatorBase
304         {
305             WindowsStreamSecurityUpgradeProvider parent;
306             IdentityVerifier identityVerifier;
307             NetworkCredential credential;
308             TokenImpersonationLevel impersonationLevel;
309             SspiSecurityTokenProvider clientTokenProvider;
310             bool allowNtlm;
311 
WindowsStreamSecurityUpgradeInitiator( WindowsStreamSecurityUpgradeProvider parent, EndpointAddress remoteAddress, Uri via)312             public WindowsStreamSecurityUpgradeInitiator(
313                 WindowsStreamSecurityUpgradeProvider parent, EndpointAddress remoteAddress, Uri via)
314                 : base(FramingUpgradeString.Negotiate, remoteAddress, via)
315             {
316                 this.parent = parent;
317                 this.clientTokenProvider = TransportSecurityHelpers.GetSspiTokenProvider(
318                     parent.securityTokenManager, remoteAddress, via, parent.Scheme, out this.identityVerifier);
319             }
320 
BaseBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)321             IAsyncResult BaseBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
322             {
323                 return base.BeginOpen(timeout, callback, state);
324             }
325 
BaseEndOpen(IAsyncResult result)326             void BaseEndOpen(IAsyncResult result)
327             {
328                 base.EndOpen(result);
329             }
330 
BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)331             internal override IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
332             {
333                 return new OpenAsyncResult(this, timeout, callback, state);
334             }
335 
EndOpen(IAsyncResult result)336             internal override void EndOpen(IAsyncResult result)
337             {
338                 OpenAsyncResult.End(result);
339             }
340 
Open(TimeSpan timeout)341             internal override void Open(TimeSpan timeout)
342             {
343                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
344                 base.Open(timeoutHelper.RemainingTime());
345                 SecurityUtils.OpenTokenProviderIfRequired(this.clientTokenProvider, timeoutHelper.RemainingTime());
346                 this.credential = TransportSecurityHelpers.GetSspiCredential(this.clientTokenProvider, timeoutHelper.RemainingTime(),
347                     out this.impersonationLevel, out this.allowNtlm);
348             }
349 
BaseBeginClose(TimeSpan timeout, AsyncCallback callback, object state)350             IAsyncResult BaseBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
351             {
352                 return base.BeginClose(timeout, callback, state);
353             }
354 
BaseEndClose(IAsyncResult result)355             void BaseEndClose(IAsyncResult result)
356             {
357                 base.EndClose(result);
358             }
359 
BeginClose(TimeSpan timeout, AsyncCallback callback, object state)360             internal override IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
361             {
362                 return new CloseAsyncResult(this, timeout, callback, state);
363             }
364 
EndClose(IAsyncResult result)365             internal override void EndClose(IAsyncResult result)
366             {
367                 CloseAsyncResult.End(result);
368             }
369 
Close(TimeSpan timeout)370             internal override void Close(TimeSpan timeout)
371             {
372                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
373                 base.Close(timeoutHelper.RemainingTime());
374                 SecurityUtils.CloseTokenProviderIfRequired(this.clientTokenProvider, timeoutHelper.RemainingTime());
375             }
376 
OnBeginInitiateUpgrade(Stream stream, AsyncCallback callback, object state)377             protected override IAsyncResult OnBeginInitiateUpgrade(Stream stream, AsyncCallback callback, object state)
378             {
379                 if (TD.WindowsStreamSecurityOnInitiateUpgradeIsEnabled())
380                 {
381                     TD.WindowsStreamSecurityOnInitiateUpgrade();
382                 }
383 
384                 InitiateUpgradeAsyncResult result = new InitiateUpgradeAsyncResult(this, callback, state);
385                 result.Begin(stream);
386                 return result;
387             }
388 
OnEndInitiateUpgrade(IAsyncResult result, out SecurityMessageProperty remoteSecurity)389             protected override Stream OnEndInitiateUpgrade(IAsyncResult result,
390                 out SecurityMessageProperty remoteSecurity)
391             {
392                 return InitiateUpgradeAsyncResult.End(result, out remoteSecurity);
393             }
394 
CreateServerSecurity(NegotiateStream negotiateStream)395             static SecurityMessageProperty CreateServerSecurity(NegotiateStream negotiateStream)
396             {
397                 GenericIdentity remoteIdentity = (GenericIdentity)negotiateStream.RemoteIdentity;
398                 string principalName = remoteIdentity.Name;
399                 if ((principalName != null) && (principalName.Length > 0))
400                 {
401                     ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = SecurityUtils.CreatePrincipalNameAuthorizationPolicies(principalName);
402                     SecurityMessageProperty result = new SecurityMessageProperty();
403                     result.TransportToken = new SecurityTokenSpecification(null, authorizationPolicies);
404                     result.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies);
405                     return result;
406                 }
407                 else
408                 {
409                     return null;
410                 }
411             }
412 
OnInitiateUpgrade(Stream stream, out SecurityMessageProperty remoteSecurity)413             protected override Stream OnInitiateUpgrade(Stream stream,
414                 out SecurityMessageProperty remoteSecurity)
415             {
416                 NegotiateStream negotiateStream;
417                 string targetName;
418                 EndpointIdentity identity;
419 
420                 if (TD.WindowsStreamSecurityOnInitiateUpgradeIsEnabled())
421                 {
422                     TD.WindowsStreamSecurityOnInitiateUpgrade();
423                 }
424 
425                 // prepare
426                 this.InitiateUpgradePrepare(stream, out negotiateStream, out targetName, out identity);
427 
428                 // authenticate
429                 try
430                 {
431                     negotiateStream.AuthenticateAsClient(credential, targetName, parent.ProtectionLevel, impersonationLevel);
432                 }
433                 catch (AuthenticationException exception)
434                 {
435                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(exception.Message,
436                         exception));
437                 }
438                 catch (IOException ioException)
439                 {
440                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(
441                         SR.GetString(SR.NegotiationFailedIO, ioException.Message), ioException));
442                 }
443 
444                 remoteSecurity = CreateServerSecurity(negotiateStream);
445                 this.ValidateMutualAuth(identity, negotiateStream, remoteSecurity, allowNtlm);
446 
447                 return negotiateStream;
448             }
449 
InitiateUpgradePrepare( Stream stream, out NegotiateStream negotiateStream, out string targetName, out EndpointIdentity identity)450             void InitiateUpgradePrepare(
451                 Stream stream,
452                 out NegotiateStream negotiateStream,
453                 out string targetName,
454                 out EndpointIdentity identity)
455             {
456                 negotiateStream = new NegotiateStream(stream);
457 
458                 targetName = string.Empty;
459                 identity = null;
460 
461                 if (parent.IdentityVerifier.TryGetIdentity(this.RemoteAddress, this.Via, out identity))
462                 {
463                     targetName = SecurityUtils.GetSpnFromIdentity(identity, this.RemoteAddress);
464                 }
465                 else
466                 {
467                     targetName = SecurityUtils.GetSpnFromTarget(this.RemoteAddress);
468                 }
469             }
470 
ValidateMutualAuth(EndpointIdentity expectedIdentity, NegotiateStream negotiateStream, SecurityMessageProperty remoteSecurity, bool allowNtlm)471             void ValidateMutualAuth(EndpointIdentity expectedIdentity, NegotiateStream negotiateStream,
472                 SecurityMessageProperty remoteSecurity, bool allowNtlm)
473             {
474                 if (negotiateStream.IsMutuallyAuthenticated)
475                 {
476                     if (expectedIdentity != null)
477                     {
478                         if (!parent.IdentityVerifier.CheckAccess(expectedIdentity,
479                             remoteSecurity.ServiceSecurityContext.AuthorizationContext))
480                         {
481                             string primaryIdentity = SecurityUtils.GetIdentityNamesFromContext(remoteSecurity.ServiceSecurityContext.AuthorizationContext);
482                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(
483                                 SR.RemoteIdentityFailedVerification, primaryIdentity)));
484                         }
485                     }
486                 }
487                 else if (!allowNtlm)
488                 {
489                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(
490                         SR.StreamMutualAuthNotSatisfied)));
491                 }
492             }
493 
494             class InitiateUpgradeAsyncResult : StreamSecurityUpgradeInitiatorAsyncResult
495             {
496                 EndpointIdentity expectedIdentity;
497                 WindowsStreamSecurityUpgradeInitiator initiator;
498                 NegotiateStream negotiateStream;
499 
InitiateUpgradeAsyncResult(WindowsStreamSecurityUpgradeInitiator initiator, AsyncCallback callback, object state)500                 public InitiateUpgradeAsyncResult(WindowsStreamSecurityUpgradeInitiator initiator,
501                     AsyncCallback callback, object state)
502                     : base(callback, state)
503                 {
504                     this.initiator = initiator;
505                 }
506 
OnBeginAuthenticateAsClient(Stream stream, AsyncCallback callback)507                 protected override IAsyncResult OnBeginAuthenticateAsClient(Stream stream, AsyncCallback callback)
508                 {
509                     string targetName;
510                     this.initiator.InitiateUpgradePrepare(stream, out this.negotiateStream, out targetName,
511                         out this.expectedIdentity);
512 
513                     return this.negotiateStream.BeginAuthenticateAsClient(this.initiator.credential, targetName,
514                             this.initiator.parent.ProtectionLevel, this.initiator.impersonationLevel, callback, this);
515                 }
516 
OnCompleteAuthenticateAsClient(IAsyncResult result)517                 protected override Stream OnCompleteAuthenticateAsClient(IAsyncResult result)
518                 {
519                     this.negotiateStream.EndAuthenticateAsClient(result);
520                     return this.negotiateStream;
521                 }
522 
ValidateCreateSecurity()523                 protected override SecurityMessageProperty ValidateCreateSecurity()
524                 {
525                     SecurityMessageProperty remoteSecurity = CreateServerSecurity(negotiateStream);
526                     this.initiator.ValidateMutualAuth(this.expectedIdentity, this.negotiateStream,
527                         remoteSecurity, this.initiator.allowNtlm);
528                     return remoteSecurity;
529                 }
530             }
531 
532             class OpenAsyncResult : AsyncResult
533             {
534                 WindowsStreamSecurityUpgradeInitiator parent;
535                 TimeoutHelper timeoutHelper;
536                 AsyncCallback onBaseOpen;
537                 AsyncCallback onOpenTokenProvider;
538                 AsyncCallback onGetSspiCredential;
539 
OpenAsyncResult(WindowsStreamSecurityUpgradeInitiator parent, TimeSpan timeout, AsyncCallback callback, object state)540                 public OpenAsyncResult(WindowsStreamSecurityUpgradeInitiator parent, TimeSpan timeout,
541                     AsyncCallback callback, object state)
542                     : base(callback, state)
543                 {
544                     this.parent = parent;
545                     TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
546 
547                     // since we're at channel.Open and not per-message, minimize our statics overhead and leverage GC for our callback
548                     this.onBaseOpen = Fx.ThunkCallback(new AsyncCallback(OnBaseOpen));
549                     this.onGetSspiCredential = Fx.ThunkCallback(new AsyncCallback(OnGetSspiCredential));
550                     this.onOpenTokenProvider = Fx.ThunkCallback(new AsyncCallback(OnOpenTokenProvider));
551                     IAsyncResult result = parent.BaseBeginOpen(timeoutHelper.RemainingTime(), onBaseOpen, this);
552 
553                     if (!result.CompletedSynchronously)
554                     {
555                         return;
556                     }
557 
558                     if (HandleBaseOpenComplete(result))
559                     {
560                         base.Complete(true);
561                     }
562                 }
563 
End(IAsyncResult result)564                 public static void End(IAsyncResult result)
565                 {
566                     AsyncResult.End<OpenAsyncResult>(result);
567                 }
568 
HandleBaseOpenComplete(IAsyncResult result)569                 bool HandleBaseOpenComplete(IAsyncResult result)
570                 {
571                     parent.BaseEndOpen(result);
572                     IAsyncResult openTokenProviderResult = SecurityUtils.BeginOpenTokenProviderIfRequired(
573                         parent.clientTokenProvider, timeoutHelper.RemainingTime(), onOpenTokenProvider, this);
574 
575                     if (!openTokenProviderResult.CompletedSynchronously)
576                     {
577                         return false;
578                     }
579 
580                     return HandleOpenTokenProviderComplete(openTokenProviderResult);
581                 }
582 
HandleOpenTokenProviderComplete(IAsyncResult result)583                 bool HandleOpenTokenProviderComplete(IAsyncResult result)
584                 {
585                     SecurityUtils.EndOpenTokenProviderIfRequired(result);
586                     IAsyncResult getCredentialResult = TransportSecurityHelpers.BeginGetSspiCredential(
587                         parent.clientTokenProvider, timeoutHelper.RemainingTime(), onGetSspiCredential, this);
588 
589                     if (!getCredentialResult.CompletedSynchronously)
590                     {
591                         return false;
592                     }
593 
594                     return HandleGetSspiCredentialComplete(getCredentialResult);
595                 }
596 
HandleGetSspiCredentialComplete(IAsyncResult result)597                 bool HandleGetSspiCredentialComplete(IAsyncResult result)
598                 {
599                     parent.credential = TransportSecurityHelpers.EndGetSspiCredential(result,
600                         out parent.impersonationLevel, out parent.allowNtlm);
601                     return true;
602                 }
603 
OnBaseOpen(IAsyncResult result)604                 void OnBaseOpen(IAsyncResult result)
605                 {
606                     if (result.CompletedSynchronously)
607                     {
608                         return;
609                     }
610 
611                     Exception completionException = null;
612                     bool completeSelf = false;
613                     try
614                     {
615                         completeSelf = this.HandleBaseOpenComplete(result);
616                     }
617 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
618                     catch (Exception e)
619                     {
620                         if (Fx.IsFatal(e))
621                         {
622                             throw;
623                         }
624 
625                         completeSelf = true;
626                         completionException = e;
627                     }
628 
629                     if (completeSelf)
630                     {
631                         base.Complete(false, completionException);
632                     }
633                 }
634 
OnOpenTokenProvider(IAsyncResult result)635                 void OnOpenTokenProvider(IAsyncResult result)
636                 {
637                     if (result.CompletedSynchronously)
638                     {
639                         return;
640                     }
641 
642                     Exception completionException = null;
643                     bool completeSelf = false;
644                     try
645                     {
646                         completeSelf = this.HandleOpenTokenProviderComplete(result);
647                     }
648 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
649                     catch (Exception e)
650                     {
651                         if (Fx.IsFatal(e))
652                         {
653                             throw;
654                         }
655 
656                         completeSelf = true;
657                         completionException = e;
658                     }
659 
660                     if (completeSelf)
661                     {
662                         base.Complete(false, completionException);
663                     }
664                 }
665 
OnGetSspiCredential(IAsyncResult result)666                 void OnGetSspiCredential(IAsyncResult result)
667                 {
668                     if (result.CompletedSynchronously)
669                     {
670                         return;
671                     }
672 
673                     Exception completionException = null;
674                     bool completeSelf = false;
675                     try
676                     {
677                         completeSelf = this.HandleGetSspiCredentialComplete(result);
678                     }
679 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
680                     catch (Exception e)
681                     {
682                         if (Fx.IsFatal(e))
683                         {
684                             throw;
685                         }
686 
687                         completeSelf = true;
688                         completionException = e;
689                     }
690 
691                     if (completeSelf)
692                     {
693                         base.Complete(false, completionException);
694                     }
695                 }
696             }
697 
698             class CloseAsyncResult : AsyncResult
699             {
700                 WindowsStreamSecurityUpgradeInitiator parent;
701                 TimeoutHelper timeoutHelper;
702                 AsyncCallback onBaseClose;
703                 AsyncCallback onCloseTokenProvider;
704 
CloseAsyncResult(WindowsStreamSecurityUpgradeInitiator parent, TimeSpan timeout, AsyncCallback callback, object state)705                 public CloseAsyncResult(WindowsStreamSecurityUpgradeInitiator parent, TimeSpan timeout,
706                     AsyncCallback callback, object state)
707                     : base(callback, state)
708                 {
709                     this.parent = parent;
710                     this.timeoutHelper = new TimeoutHelper(timeout);
711 
712                     // since we're at channel.Open and not per-message, minimize our statics overhead and leverage GC for our callback
713                     this.onBaseClose = Fx.ThunkCallback(new AsyncCallback(OnBaseClose));
714                     this.onCloseTokenProvider = Fx.ThunkCallback(new AsyncCallback(OnCloseTokenProvider));
715                     IAsyncResult result = parent.BaseBeginClose(timeoutHelper.RemainingTime(), onBaseClose, this);
716 
717                     if (!result.CompletedSynchronously)
718                     {
719                         return;
720                     }
721 
722                     if (HandleBaseCloseComplete(result))
723                     {
724                         base.Complete(true);
725                     }
726                 }
727 
End(IAsyncResult result)728                 public static void End(IAsyncResult result)
729                 {
730                     AsyncResult.End<CloseAsyncResult>(result);
731                 }
732 
HandleBaseCloseComplete(IAsyncResult result)733                 bool HandleBaseCloseComplete(IAsyncResult result)
734                 {
735                     parent.BaseEndClose(result);
736                     IAsyncResult closeTokenProviderResult = SecurityUtils.BeginCloseTokenProviderIfRequired(
737                         parent.clientTokenProvider, timeoutHelper.RemainingTime(), onCloseTokenProvider, this);
738 
739                     if (!closeTokenProviderResult.CompletedSynchronously)
740                     {
741                         return false;
742                     }
743 
744                     SecurityUtils.EndCloseTokenProviderIfRequired(closeTokenProviderResult);
745                     return true;
746                 }
747 
OnBaseClose(IAsyncResult result)748                 void OnBaseClose(IAsyncResult result)
749                 {
750                     if (result.CompletedSynchronously)
751                     {
752                         return;
753                     }
754 
755                     Exception completionException = null;
756                     bool completeSelf = false;
757                     try
758                     {
759                         completeSelf = this.HandleBaseCloseComplete(result);
760                     }
761 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
762                     catch (Exception e)
763                     {
764                         if (Fx.IsFatal(e))
765                         {
766                             throw;
767                         }
768 
769                         completeSelf = true;
770                         completionException = e;
771                     }
772 
773                     if (completeSelf)
774                     {
775                         base.Complete(false, completionException);
776                     }
777                 }
778 
OnCloseTokenProvider(IAsyncResult result)779                 void OnCloseTokenProvider(IAsyncResult result)
780                 {
781                     if (result.CompletedSynchronously)
782                     {
783                         return;
784                     }
785 
786                     Exception completionException = null;
787                     try
788                     {
789                         SecurityUtils.EndCloseTokenProviderIfRequired(result);
790                     }
791 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
792                     catch (Exception e)
793                     {
794                         if (Fx.IsFatal(e))
795                         {
796                             throw;
797                         }
798 
799                         completionException = e;
800                     }
801 
802                     base.Complete(false, completionException);
803                 }
804             }
805         }
806     }
807 }
808