Adding reference source for System.Net
[mono.git] / mcs / class / referencesource / System / net / System / Net / _SecureChannel.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="_SecureChannel.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net.Security {
8     using System.Diagnostics;
9     using System.Net;
10     using System.Net.Sockets;
11     using System.Runtime.InteropServices;
12     using System.Security.Authentication.ExtendedProtection;
13     using System.Security.Cryptography;
14     using System.Security.Cryptography.X509Certificates;
15     using System.Text;
16     using System.Threading;
17     using System.Security.Permissions;
18     using System.Globalization;
19     using System.ComponentModel;
20     using System.Security.Principal;
21     using System.Security;
22     using System.Collections;
23
24     //
25     // SecureChannel - a wrapper on SSPI based functionality,
26     //  provides an additional abstraction layer over SSPI
27     //  for the SSL Stream to utilize.
28     //
29
30     internal class SecureChannel {
31         //also used as a lock object
32         internal const string SecurityPackage = "Microsoft Unified Security Protocol Provider";
33         private  static readonly object s_SyncObject = new object();
34
35         private const ContextFlags RequiredFlags =
36             ContextFlags.ReplayDetect |
37             ContextFlags.SequenceDetect |
38             ContextFlags.Confidentiality|
39             ContextFlags.AllocateMemory;
40
41
42         private const ContextFlags ServerRequiredFlags =
43             RequiredFlags
44             | ContextFlags.AcceptStream
45             // | ContextFlags.AcceptExtendedError
46             ;
47
48         private const int           ChainRevocationCheckExcludeRoot = 0x40000000;
49
50         // When reading a frame from the wire first read this many bytes for the header.
51         internal const int ReadHeaderSize = 5;
52
53         private static volatile X509Store s_MyCertStoreEx;
54         private static volatile X509Store s_MyMachineCertStoreEx;
55
56         private SafeFreeCredentials m_CredentialsHandle;
57         private SafeDeleteContext   m_SecurityContext;
58         private ContextFlags        m_Attributes;
59         private readonly string     m_Destination;
60         private readonly string     m_HostName;
61
62         private readonly bool       m_ServerMode;
63         private readonly bool       m_RemoteCertRequired;
64         private readonly SchProtocols m_ProtocolFlags;
65         private readonly EncryptionPolicy m_EncryptionPolicy;
66         private SslConnectionInfo   m_ConnectionInfo;
67
68         private X509Certificate     m_ServerCertificate;
69         private X509Certificate     m_SelectedClientCertificate;
70         private bool                m_IsRemoteCertificateAvailable;
71
72         private readonly X509CertificateCollection m_ClientCertificates;
73         private LocalCertSelectionCallback m_CertSelectionDelegate;
74
75         // These are the MAX encrypt buffer output sizes, not the actual sizes.
76         private int                 m_HeaderSize    = 5; //ATTN must be set to at least 5 by default
77         private int                 m_TrailerSize   = 16;
78         private int                 m_MaxDataSize   = 16354;
79
80         private bool                m_CheckCertRevocation;
81         private bool                m_CheckCertName;
82
83         private bool                m_RefreshCredentialNeeded;
84
85
86         internal SecureChannel(string hostname, bool serverMode, SchProtocols protocolFlags, X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool remoteCertRequired, bool checkCertName, 
87                                                   bool checkCertRevocationStatus, EncryptionPolicy encryptionPolicy, LocalCertSelectionCallback certSelectionDelegate)
88         {
89             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::.ctor", "hostname:" + hostname + " #clientCertificates=" + ((clientCertificates == null) ? "0" : clientCertificates.Count.ToString(NumberFormatInfo.InvariantInfo)));
90             if (Logging.On) Logging.PrintInfo(Logging.Web, this, ".ctor", "hostname=" + hostname + ", #clientCertificates=" + ((clientCertificates == null) ? "0" : clientCertificates.Count.ToString(NumberFormatInfo.InvariantInfo)) + ", encryptionPolicy=" + encryptionPolicy);
91             SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPISecureChannel, SecurityPackage, true);
92
93             m_Destination = hostname;
94
95             GlobalLog.Assert(hostname != null, "SecureChannel#{0}::.ctor()|hostname == null", ValidationHelper.HashString(this));
96             m_HostName = hostname;
97             m_ServerMode = serverMode;
98
99             if (serverMode)
100                 m_ProtocolFlags = (protocolFlags & SchProtocols.ServerMask);
101             else
102                 m_ProtocolFlags = (protocolFlags & SchProtocols.ClientMask);
103
104             m_ServerCertificate = serverCertificate;
105             m_ClientCertificates = clientCertificates;
106             m_RemoteCertRequired = remoteCertRequired;
107             m_SecurityContext = null;
108             m_CheckCertRevocation = checkCertRevocationStatus;
109             m_CheckCertName = checkCertName;
110             m_CertSelectionDelegate = certSelectionDelegate;
111             m_RefreshCredentialNeeded = true;
112             m_EncryptionPolicy = encryptionPolicy;
113             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::.ctor");
114         }
115
116         //
117         // SecureChannel properties
118         //
119         //   LocalServerCertificate - local certificate for server mode channel
120         //   LocalClientCertificate - selected certificated used in the client channel mode otherwise null
121         //   IsRemoteCertificateAvailable - true if the remote side has provided a certificate
122         //   HeaderSize             - Header & trailer sizes used in the TLS stream
123         //   TrailerSize -
124         //
125         internal X509Certificate LocalServerCertificate {
126                 get {
127                 return m_ServerCertificate;
128             }
129         }
130
131         internal X509Certificate LocalClientCertificate {
132             get {
133                 return m_SelectedClientCertificate;
134             }
135         }
136
137         internal bool IsRemoteCertificateAvailable {
138             get {
139                 return m_IsRemoteCertificateAvailable;
140             }
141         }
142
143
144         unsafe static class UnmanagedCertificateContext
145         {
146
147             [StructLayout(LayoutKind.Sequential)]
148             private struct _CERT_CONTEXT {
149                 internal Int32     dwCertEncodingType;
150                 internal IntPtr    pbCertEncoded;
151                 internal Int32     cbCertEncoded;
152                 internal IntPtr    pCertInfo;
153                 internal IntPtr    hCertStore;
154             };
155
156             internal static X509Certificate2Collection GetStore(SafeFreeCertContext certContext)
157             {
158                 X509Certificate2Collection result = new X509Certificate2Collection();
159
160                 if (certContext.IsInvalid)
161                     return result;
162
163                 _CERT_CONTEXT context = (_CERT_CONTEXT)Marshal.PtrToStructure(certContext.DangerousGetHandle(), typeof(_CERT_CONTEXT));
164
165                 if (context.hCertStore != IntPtr.Zero)
166                 {
167                     X509Store store = null;
168                     try {
169                         store = new X509Store(context.hCertStore);
170                         result = store.Certificates;
171                     }
172                     finally {
173                         if (store != null)
174                             store.Close();
175                     }
176                 }
177                 return result;
178             }
179         }
180         //
181         //This code extracts a remote certificate upon request.
182         //SECURITY: The scenario is allowed in semitrust
183         //
184         internal X509Certificate2 GetRemoteCertificate(out X509Certificate2Collection remoteCertificateStore)
185         {
186             remoteCertificateStore = null;
187
188             if (m_SecurityContext == null)
189                 return null;
190
191             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::RemoteCertificate{get;}");
192             X509Certificate2 result = null;
193             SafeFreeCertContext remoteContext = null;
194             try {
195                 remoteContext = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPISecureChannel, m_SecurityContext, ContextAttribute.RemoteCertificate) as SafeFreeCertContext;
196                 if (remoteContext != null && !remoteContext.IsInvalid) {
197                     result = new X509Certificate2(remoteContext.DangerousGetHandle());
198                 }
199             }
200             finally {
201                 if (remoteContext != null) {
202                     remoteCertificateStore = UnmanagedCertificateContext.GetStore(remoteContext);
203                     remoteContext.Close();
204                 }
205             }
206
207             if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_log_remote_certificate, (result == null ? "null" : result.ToString(true))));
208             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::RemoteCertificate{get;}", (result == null? "null" :result.Subject));
209             
210             return result;
211         }
212
213         internal ChannelBinding GetChannelBinding(ChannelBindingKind kind)
214         {
215             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::GetChannelBindingToken", kind.ToString());
216
217             ChannelBinding result = null;
218             if (m_SecurityContext != null)
219             {
220                 result = SSPIWrapper.QueryContextChannelBinding(GlobalSSPI.SSPISecureChannel, m_SecurityContext, (ContextAttribute)kind);
221             }
222
223             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::GetChannelBindingToken", ValidationHelper.HashString(result));
224             return result;
225         }
226
227         internal bool CheckCertRevocationStatus {
228             get {
229                 return m_CheckCertRevocation;
230             }
231         }
232
233         internal X509CertificateCollection ClientCertificates {
234             get {
235                 return m_ClientCertificates;
236             }
237         }
238
239         internal int HeaderSize {
240             get {
241                 return m_HeaderSize;
242             }
243         }
244
245         internal int MaxDataSize {
246             get {
247                 return m_MaxDataSize;
248             }
249         }
250
251         internal SslConnectionInfo ConnectionInfo {
252             get {
253                 return m_ConnectionInfo;
254             }
255         }
256
257         internal bool IsValidContext {
258             get {
259                 return !(m_SecurityContext == null || m_SecurityContext.IsInvalid);
260             }
261         }
262
263         internal bool IsServer {
264             get {
265                 return m_ServerMode;
266             }
267         }
268
269         internal bool RemoteCertRequired {
270             get {
271                 return m_RemoteCertRequired;
272             }
273         }
274
275         internal void SetRefreshCredentialNeeded()
276         {
277             m_RefreshCredentialNeeded = true;
278         }
279
280         internal void Close() {
281             if (m_SecurityContext != null) {
282                 m_SecurityContext.Close();
283             }
284             if (m_CredentialsHandle != null) {
285               m_CredentialsHandle.Close();
286             }
287         }
288
289         //
290         // SECURITY: we open a private key container on behalf of the caller
291         // and we require the caller to have permission associated with that operation.
292         // After discussing with X509Certificate2 owners decided to demand KeyContainerPermission (Open)
293         // At the same time we assert StorePermission on the caller frame since for consistency
294         // we cannot predict when it will be demanded (SSL session reuse feature)
295         //
296         X509Certificate2 EnsurePrivateKey(X509Certificate certificate)
297         {
298             if (certificate == null)
299                 return null;
300
301             if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_locating_private_key_for_certificate, certificate.ToString(true)));
302             
303             try {
304                 X509Certificate2 certEx = certificate as X509Certificate2;
305                 Type t = certificate.GetType();
306                 string certHash = null;
307
308                 // Protecting from X509Certificate2 derived classes
309                 if (t != typeof(X509Certificate2) && t != typeof(X509Certificate))
310                 {
311                     if (certificate.Handle != IntPtr.Zero)
312                     {
313                         certEx = new X509Certificate2(certificate);
314                         certHash = certEx.GetCertHashString();
315                     }
316                 }
317                 else
318                 {
319                     certHash = certificate.GetCertHashString();
320                 }
321
322                 if (certEx != null)
323                 {
324                     if (certEx.HasPrivateKey)
325                     {
326                         if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_cert_is_of_type_2));
327                         return certEx;
328                     }
329
330                     if ((object)certificate != (object) certEx)
331                         certEx.Reset();
332                 }
333
334                 //
335                 // The certificate doesn't have a private key, so we need
336                 // to open the store and search for it there. Demand the
337                 // KeyContainerPermission with Open access before opening
338                 // the store. If store.Open() or store.Cert.Find()
339                 // demand the same permissions, then we should remove our
340                 // demand here.
341                 //
342                 ExceptionHelper.KeyContainerPermissionOpen.Demand(); 
343                 
344                 X509Certificate2Collection collectionEx;
345
346                 // ELSE Try MY user and machine stores for private key check
347                 // For server side mode MY machine store takes priority
348                 X509Store store = EnsureStoreOpened(m_ServerMode);
349                 if (store != null)
350                 {
351                     collectionEx = store.Certificates.Find(X509FindType.FindByThumbprint, certHash, false);
352                     if (collectionEx.Count > 0 && collectionEx[0].PrivateKey != null)
353                     {
354                         if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_found_cert_in_store, (m_ServerMode ? "LocalMachine" : "CurrentUser")));
355                         return collectionEx[0];
356                     }
357                 }
358
359                 store = EnsureStoreOpened(!m_ServerMode);
360                 if (store != null)
361                 {
362                     collectionEx = store.Certificates.Find(X509FindType.FindByThumbprint, certHash, false);
363                     if (collectionEx.Count > 0 && collectionEx[0].PrivateKey != null)
364                     {
365                         if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_found_cert_in_store, (m_ServerMode ? "CurrentUser" : "LocalMachine")));
366                         return collectionEx[0];
367                     }
368                 }
369             }
370             catch (CryptographicException) {
371             }
372
373             if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_did_not_find_cert_in_store));
374
375             return null;
376         }
377         //
378         // Security: we temporarily reset thread token to open the cert store under process acount
379         //
380         [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.ControlPrincipal)]
381         internal static X509Store EnsureStoreOpened(bool isMachineStore)
382         {
383             X509Store store = isMachineStore? s_MyMachineCertStoreEx: s_MyCertStoreEx;
384             if (store == null) {
385                 lock (s_SyncObject) {
386                     store = isMachineStore? s_MyMachineCertStoreEx: s_MyCertStoreEx;
387                     if (store==null) {
388                         // NOTE: that if this call fails we won't keep track and the next time we enter we will try to open the store again
389                         StoreLocation storeLocation = isMachineStore? StoreLocation.LocalMachine: StoreLocation.CurrentUser;
390                         store = new X509Store(StoreName.My, storeLocation);
391                         try {
392                             //
393                             // For v 1.1 compat We want to ensure the store is opened under the **process** acount.
394                             //
395                             try {
396                                 using (WindowsIdentity.Impersonate(IntPtr.Zero))
397                                 {
398                                     store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
399                                     GlobalLog.Print("SecureChannel::EnsureStoreOpened() storeLocation:" + storeLocation + " returned store:" + store.GetHashCode().ToString("x"));
400                                 }
401                             } catch {
402                                 throw;
403                             }
404
405                             if (isMachineStore)
406                                 s_MyMachineCertStoreEx = store;
407                             else
408                                 s_MyCertStoreEx = store;
409
410                             return store;
411                         }
412                         catch (Exception exception) {
413                             if (exception is CryptographicException || exception is SecurityException) {
414                                 GlobalLog.Assert("SecureChannel::EnsureStoreOpened()", "Failed to open cert store, location:" + storeLocation + " exception:" + exception);
415                                 return null;
416                             }
417                             if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_open_store_failed, storeLocation, exception));
418                             throw;
419                         }
420                     }
421                 }
422             }
423             return store;
424         }
425
426         //
427         // Returns:
428         //   For the input type == X509Certificate2 cert the same ref
429         //   For an older cert format clones it as X509Certificate2 and returns result
430         //   For a derived type clones it as X509Certificate2 and returns result
431         //
432         static X509Certificate2 MakeEx(X509Certificate certificate)
433         {
434             if (certificate.GetType() == typeof(X509Certificate2))
435                 return (X509Certificate2)certificate;
436
437             X509Certificate2 certificateEx = null;
438             try {
439                 if (certificate.Handle!=IntPtr.Zero) {
440                     certificateEx = new X509Certificate2(certificate);
441                 }
442             }
443             catch (SecurityException) {
444             }
445             catch (CryptographicException) {
446             }
447             return certificateEx;
448         }
449
450         //
451         // Used only by client SSL code, never returns null.
452         //
453         private string[] GetIssuers()
454         {
455             string[] issuers = new string[0];
456
457             if (IsValidContext)
458             {
459                 IssuerListInfoEx issuerList = (IssuerListInfoEx)SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPISecureChannel, m_SecurityContext, ContextAttribute.IssuerListInfoEx);
460                 try
461                 {
462                     if (issuerList.cIssuers>0) {
463                         unsafe {
464                             uint count = issuerList.cIssuers;
465                             issuers = new string[issuerList.cIssuers];
466                             _CERT_CHAIN_ELEMENT* pIL = (_CERT_CHAIN_ELEMENT*)issuerList.aIssuers.DangerousGetHandle();
467                             for (int i =0; i<count; ++i) {
468                                 _CERT_CHAIN_ELEMENT* pIL2 = pIL + i;
469                                 uint size = pIL2->cbSize;
470                                 byte* ptr = (byte*)(pIL2->pCertContext);
471                                 byte[] x = new byte[size];
472                                 for (int j=0; j<size; j++) {
473                                     x[j] = *(ptr + j);
474                                 }
475                                 // Oid oid = new Oid();
476                                 // oid.Value = "1.3.6.1.5.5.7.3.2";
477                                 X500DistinguishedName x500DistinguishedName = new X500DistinguishedName(x);
478                                 issuers[i] = x500DistinguishedName.Name;
479                                 GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::GetIssuers() IssuerListEx[" + i + "]:" + issuers[i]);
480                             }
481                         }
482                     }
483                 }
484                 finally
485                 {
486                     if (issuerList.aIssuers != null)
487                     {
488                         issuerList.aIssuers.Close();
489                     }
490                 }
491             }
492             return issuers;
493         }
494         /*++
495             AcquireCredentials - Attempts to find Client Credential
496             Information, that can be sent to the server.  In our case,
497             this is only Client Certificates, that we have Credential Info.
498
499             Here is how we work:
500                 case 0: Cert Selection delegate is present
501                         Alwasys use its result as the client cert answer.
502                         Try to use cached credential handle whenever feasible.
503                         Do not use cached anonymous creds if the delegate has returned null
504                         and the collection is not empty (allow responding with the cert later).
505
506                 case 1: Certs collection is empty
507                         Always use the same statically acquired anonymous SSL Credential
508
509                 case 2: Before our Connection with the Server
510                         If we have a cached credential handle keyed by first X509Certificate
511                         **content** in the passed collection, then we use that cached
512                         credential and hoping to restart a session.
513
514                         Otherwise create a new anonymous (allow responding with the cert later).
515
516                 case 3: After our Connection with the Server (ie during handshake or re-handshake)
517                         The server has requested that we send it a Certificate then
518                         we Enumerate a list of server sent Issuers trying to match against
519                         our list of Certificates, the first match is sent to the server.
520
521                         Once we got a cert we again try to match cached credential handle if possible.
522                         This will not restart a session but helps miminizing the number of handles we create.
523
524                 In the case of an error getting a Certificate or checking its private Key we fall back
525                 to the behavior of having no certs, case 1
526
527             Returns: True if cached creds were used, false otherwise
528
529         --*/
530         //
531         //SECURITY: The permission assert is needed for Chain.Build and for certs enumeration.
532         //          The user will see KeyContainerPermission demand in the case where the client
533         //          cert as about to be used.
534         //          Note: We call a user certificate selection delegate under permission
535         //          assert but the signature of the delegate is unique so it's safe
536         //
537         [StorePermission(SecurityAction.Assert, Unrestricted=true)]
538         private bool AcquireClientCredentials(ref byte[] thumbPrint)
539         {
540             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials");
541
542             //
543             // Acquire possible Client Certificate information and set it on the handle
544             //
545
546             X509Certificate clientCertificate = null;   // This is a candidate that can come from the user callback or be guessed when targeting a session restart
547             ArrayList filteredCerts = new ArrayList();  // This is an intermediate client certs collection that try to use if no selectedCert is available yet.
548             string[] issuers = null;                    // This is a list of issuers sent by the server, only valid is we do know what the server cert is.
549
550             bool sessionRestartAttempt = false; // if true and no cached creds we will use anonymous creds.
551
552             if (m_CertSelectionDelegate!=null)
553             {
554                 if (issuers == null)
555                     issuers = GetIssuers();
556
557                 GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() calling CertificateSelectionCallback");
558                 
559                 X509Certificate2 remoteCert = null;
560                 try {
561                     X509Certificate2Collection dummyCollection;
562                     remoteCert = GetRemoteCertificate(out dummyCollection);
563                     clientCertificate = m_CertSelectionDelegate(m_HostName, ClientCertificates, remoteCert, issuers);
564                 }
565                 finally {
566                     if (remoteCert != null)
567                         remoteCert.Reset();
568                 }
569
570
571                 if (clientCertificate != null)
572                 {
573                     if (m_CredentialsHandle == null)
574                         sessionRestartAttempt = true;
575                     filteredCerts.Add(clientCertificate);
576                     if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_got_certificate_from_delegate));
577                 }
578                 else 
579                 {
580                     // If ClientCertificates.Count != 0, how come we don't try to go through them and add them to the filtered certs, just like when there is no delegate????
581                     if (ClientCertificates.Count == 0)
582                     {
583                         if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_no_delegate_and_have_no_client_cert));
584                         sessionRestartAttempt = true;
585                     }
586                     else
587                     {
588                         if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_no_delegate_but_have_client_cert));
589                     }
590                 }
591
592             }
593             else if (m_CredentialsHandle == null && m_ClientCertificates != null && m_ClientCertificates.Count > 0)
594             {
595                 // This is where we attempt to restart a session by picking the FIRST cert from the collection.
596                 // Otheriwse (next elses) it is either server sending a client cert request or the session is renegotiated.
597                 clientCertificate = ClientCertificates[0];
598                 sessionRestartAttempt = true;
599                 if (clientCertificate!=null)
600                     filteredCerts.Add(clientCertificate);
601                 if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_attempting_restart_using_cert, (clientCertificate == null ? "null" : clientCertificate.ToString(true))));
602             }
603             else if (m_ClientCertificates!=null && m_ClientCertificates.Count > 0)
604             {
605                 //
606                 // This should be a server request for the client cert sent over currently anonyumous sessions.
607                 //
608                 if (issuers == null)
609                     issuers = GetIssuers();
610
611
612                 if (Logging.On) 
613                 {
614                     if (issuers == null || issuers.Length == 0)
615                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_no_issuers_try_all_certs));
616                     else
617                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_server_issuers_look_for_matching_certs, issuers.Length));
618                 }
619
620                 for (int i = 0; i < m_ClientCertificates.Count; ++i)
621                 {
622                     //
623                     // make sure we add only if the cert matches one of the issuers
624                     // If no issuers were sent and then try all client certs starting with the first one.
625                     //
626                     if (issuers != null && issuers.Length != 0)
627                     {
628                         X509Certificate2 certificateEx = null;
629                         X509Chain chain = null;
630                         try {
631                             certificateEx = MakeEx(m_ClientCertificates[i]);
632                             if (certificateEx == null)
633                                 continue;
634
635                             GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() root cert:" + certificateEx.Issuer);
636                             chain = new X509Chain();
637
638                             chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
639                             chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreInvalidName;
640                             chain.Build(certificateEx);
641                             bool found = false;
642
643                             //
644                             // We ignore any errors happened with chain.
645                             // Consider: try to locate the "best" client cert that has no errors and the lognest validity internal
646                             //
647                             if (chain.ChainElements.Count > 0)
648                             {
649                                 for (int ii=0; ii< chain.ChainElements.Count; ++ii)
650                                 {
651                                     string issuer = chain.ChainElements[ii].Certificate.Issuer;
652                                     found = Array.IndexOf(issuers, issuer)!=-1;
653                                     if (found) {
654                                         GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() matched:" + issuer);
655                                         break;
656                                     }
657                                     GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() no match:" + issuer);
658                                 }
659                             }
660                             if (!found) {
661                                 continue;
662                             }
663                         }
664                         finally {
665                             if (chain != null)
666                                 chain.Reset();
667
668                             if (certificateEx != null && (object)certificateEx != (object)m_ClientCertificates[i])
669                                 certificateEx.Reset();
670                         }
671                     }
672                     if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_selected_cert, m_ClientCertificates[i].ToString(true)));
673                     filteredCerts.Add(m_ClientCertificates[i]);
674                 }
675             }
676
677             bool cachedCred = false;                    // This is a return result from this method
678             X509Certificate2 selectedCert = null;      // This is a final selected cert (ensured that it does have private key with it)
679
680             clientCertificate = null;
681
682             if (Logging.On) {
683                 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_n_certs_after_filtering, filteredCerts.Count));
684                 if (filteredCerts.Count != 0) 
685                     Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_finding_matching_certs));
686             }
687
688             //
689             // ATTN: When the client cert was returned by the user callback OR it was guessed AND it has no private key.
690             //       THEN anonymous (no client cert) credential will be used
691             //
692             // SECURITY: Accessing X509 cert Credential is disabled for semitrust
693             // We no longer need to demand for unmanaged code permissions.
694             // EnsurePrivateKey should do the right demand for us.
695             for (int i=0; i < filteredCerts.Count; ++i)
696             {
697                 clientCertificate = filteredCerts[i] as X509Certificate;
698                 if ((selectedCert = EnsurePrivateKey(clientCertificate)) != null)
699                     break;
700                 clientCertificate = null;
701                 selectedCert = null;
702             }
703
704             GlobalLog.Assert(((object) clientCertificate == (object) selectedCert) || clientCertificate.Equals(selectedCert), "AcquireClientCredentials()|'selectedCert' does not match 'clientCertificate'.");
705
706             GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() Selected Cert = " + (selectedCert == null? "null": selectedCert.Subject));
707             try {
708                 // Try to locate cached creds first.
709                 //
710                 // SECURITY: selectedCert ref if not null is a safe object that does not depend on possible **user** inherited X509Certificate type.
711                 //
712                 byte[] guessedThumbPrint = selectedCert == null? null: selectedCert.GetCertHash();
713                 SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, m_ProtocolFlags, m_EncryptionPolicy);
714
715                 // We can probably do some optimization here. If the selectedCert is returned by the delegate
716                 // we can always go ahead and use the certificate to create our credential
717                 // (Instead of going anonymous as we do here)
718                 if (sessionRestartAttempt && cachedCredentialHandle == null && selectedCert != null)
719                 {
720                     GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() Reset to anonymous session.");
721
722                     // (see VsWhidbey#363953) For some (probably good) reason IIS does not renegotiate a restarted session if client cert is needed.
723                     // So we don't want to reuse **anonymous** cached credential for a new SSL connection if the client has passed some certificate.
724                     // The following block happens if client did specify a certificate but no cached creds were found in the cache
725                     // Since we don't restart a session the server side can still challenge for a client cert.
726                     if ((object)clientCertificate != (object)selectedCert)
727                         selectedCert.Reset();
728                     guessedThumbPrint = null;
729                     selectedCert = null;
730                     clientCertificate = null;
731                 }
732
733                 if (cachedCredentialHandle != null)
734                 {
735                     if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_log_using_cached_credential));
736                     m_CredentialsHandle = cachedCredentialHandle;
737                     m_SelectedClientCertificate = clientCertificate;
738                     cachedCred = true;
739                 }
740                 else
741                 {
742                     SecureCredential.Flags flags = SecureCredential.Flags.ValidateManual | SecureCredential.Flags.NoDefaultCred;
743                     if (!ServicePointManager.DisableStrongCrypto 
744                         && ((m_ProtocolFlags & (SchProtocols.Tls10 | SchProtocols.Tls11 | SchProtocols.Tls12)) != 0))
745                     {
746                         flags |= SecureCredential.Flags.UseStrongCrypto;
747                     }
748
749                     SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, selectedCert, flags, m_ProtocolFlags, m_EncryptionPolicy);
750                     m_CredentialsHandle = AcquireCredentialsHandle(CredentialUse.Outbound, ref secureCredential);
751                     thumbPrint = guessedThumbPrint; //delay it until here in case something above threw
752                     m_SelectedClientCertificate = clientCertificate;
753                 }
754
755             }
756             finally {
757                 // an extra cert could have been created, dispose it now
758                 if (selectedCert != null && (object)clientCertificate != (object)selectedCert)
759                     selectedCert.Reset();
760             }
761
762             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials, cachedCreds = " + cachedCred.ToString(), ValidationHelper.ToString(m_CredentialsHandle));
763             return cachedCred;
764         }
765
766
767         //
768         // Acquire Server Side Certificate information and set it on the class
769         //
770         //
771         //SECURITY: The permission assert is needed for Chain.Build and for certs enumeration.
772         //          The user will see KeyContainerPermission demand in the case where the server
773         //          cert as about to be used.
774         //          Note: We call a user certificate selection delegate under permission
775         //          assert but the signature of the delegate is unique so it's safe
776         //
777         [StorePermission(SecurityAction.Assert, Unrestricted=true)]
778         private bool AcquireServerCredentials(ref byte[] thumbPrint)
779         {
780             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireServerCredentials");
781
782             X509Certificate localCertificate = null;
783             bool cachedCred = false;
784
785             if (m_CertSelectionDelegate != null)
786             {
787                 X509CertificateCollection tempCollection = new X509CertificateCollection();
788                 tempCollection.Add(m_ServerCertificate);
789                 localCertificate = m_CertSelectionDelegate(string.Empty, tempCollection, null, new string[0]);
790                 GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireServerCredentials() Use delegate selected Cert");
791             }
792             else
793             {
794                 localCertificate = m_ServerCertificate;
795             }
796
797             if (localCertificate == null)
798                 throw new NotSupportedException(SR.GetString(SR.net_ssl_io_no_server_cert));
799
800             // SECURITY: Accessing X509 cert Credential is disabled for semitrust
801             // We no longer need to demand for unmanaged code permissions.
802             // EnsurePrivateKey should do the right demand for us.
803             X509Certificate2 selectedCert = EnsurePrivateKey(localCertificate);
804
805             if (selectedCert == null)
806                 throw new NotSupportedException(SR.GetString(SR.net_ssl_io_no_server_cert));
807
808             GlobalLog.Assert(localCertificate.Equals(selectedCert), "AcquireServerCredentials()|'selectedCert' does not match 'localCertificate'.");
809
810             //
811             // Note selectedCert is a safe ref possibly cloned from the user passed Cert object
812             //
813             byte [] guessedThumbPrint = selectedCert.GetCertHash();
814             try {
815                 SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, m_ProtocolFlags, m_EncryptionPolicy);
816
817                 if (cachedCredentialHandle != null)
818                 {
819                     m_CredentialsHandle = cachedCredentialHandle;
820                     m_ServerCertificate = localCertificate;
821                     cachedCred = true;
822                 }
823                 else
824                 {
825                     SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, selectedCert, SecureCredential.Flags.Zero, m_ProtocolFlags, m_EncryptionPolicy);
826                     m_CredentialsHandle = AcquireCredentialsHandle(CredentialUse.Inbound, ref secureCredential);
827                     thumbPrint = guessedThumbPrint;
828                     m_ServerCertificate = localCertificate;
829                 }
830
831             }
832             finally {
833                 // an extra cert could have been created, dispose it now
834                 if ((object)localCertificate != (object)selectedCert)
835                     selectedCert.Reset();
836             }
837
838             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireServerCredentials, cachedCreds = " + cachedCred.ToString(), ValidationHelper.ToString(m_CredentialsHandle));
839             return cachedCred;
840         }
841
842
843         //
844         // Security: we temporarily reset thread token to open the handle under process acount
845         //
846         [SecurityPermissionAttribute(SecurityAction.Assert, Flags=SecurityPermissionFlag.ControlPrincipal)]
847         SafeFreeCredentials AcquireCredentialsHandle(CredentialUse credUsage, ref SecureCredential secureCredential)
848         {
849             // First try without impersonation, if it fails, then try the process account.
850             // I.E. We don't know which account the certificate context was created under.
851             try {
852                 //
853                 // For v 1.1 compat We want to ensure the credential are accessed under >>process<< acount.
854                 //
855                 using (WindowsIdentity.Impersonate(IntPtr.Zero))
856                 {
857                     return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential);
858                 }
859             } catch {
860                 return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential);
861             }
862         }
863
864         //
865         internal ProtocolToken NextMessage(byte[] incoming, int offset, int count) {
866             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::NextMessage");
867             byte[] nextmsg = null;
868             SecurityStatus errorCode = GenerateToken(incoming, offset, count, ref nextmsg);
869
870             if (!m_ServerMode && errorCode == SecurityStatus.CredentialsNeeded)
871             {
872                 GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::NextMessage() returned SecurityStatus.CredentialsNeeded");
873                 SetRefreshCredentialNeeded();
874                 errorCode = GenerateToken(incoming, offset, count, ref nextmsg);
875             }
876             ProtocolToken token = new ProtocolToken(nextmsg, errorCode);
877             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::NextMessage", token.ToString());
878             return token;
879         }
880
881         /*++
882             GenerateToken - Called after each sucessive state
883             in the Client - Server handshake.  This function
884             generates a set of bytes that will be sent next to
885             the server.  The server responds, each response,
886             is pass then into this function, again, and the cycle
887             repeats until successful connection, or failure.
888
889             Input:
890                 input  - bytes from the wire
891                 output - ref to byte [], what we will send to the
892                     server in response
893             Return:
894                 errorCode - an SSPI error code
895
896         --*/
897         private SecurityStatus GenerateToken(byte[] input, int offset, int count, ref byte[] output)
898         {
899             
900 #if TRAVE
901             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::GenerateToken, m_RefreshCredentialNeeded = " + m_RefreshCredentialNeeded);
902 #endif
903
904             if (offset < 0 || offset > (input == null ? 0 : input.Length))
905             {
906                 GlobalLog.Assert(false, "SecureChannel#" + ValidationHelper.HashString(this) + "::GenerateToken", "Argument 'offset' out of range.");
907                 throw new ArgumentOutOfRangeException("offset");
908             }
909             if (count < 0 || count > (input == null ? 0 : input.Length - offset))
910             {
911                 GlobalLog.Assert(false, "SecureChannel#" + ValidationHelper.HashString(this) + "::GenerateToken", "Argument 'count' out of range.");
912                 throw new ArgumentOutOfRangeException("count");
913             }
914
915             SecurityBuffer incomingSecurity = null;
916             SecurityBuffer[] incomingSecurityBuffers = null;
917
918             if (input != null) {
919                 incomingSecurity = new SecurityBuffer(input, offset, count, BufferType.Token);
920                 incomingSecurityBuffers = new SecurityBuffer[]
921                 {
922                     incomingSecurity,
923                     new SecurityBuffer(null, 0, 0, BufferType.Empty)
924                 };
925             }
926
927             SecurityBuffer outgoingSecurity = new SecurityBuffer(null, BufferType.Token);
928
929             int errorCode = 0;
930
931             bool cachedCreds = false;
932             byte[] thumbPrint = null;
933             //
934             // Looping through ASC or ISC with potenially cached credential that could have been
935             // already disposed from a different thread before ISC or ASC dir increemnt a cred ref count.
936             //
937             try
938             {
939                 do
940                 {
941                     thumbPrint = null;
942                     if (m_RefreshCredentialNeeded)
943                     {
944                         cachedCreds = m_ServerMode
945                                         ? AcquireServerCredentials(ref thumbPrint)
946                                         : AcquireClientCredentials(ref thumbPrint);
947                     }
948
949                     if (m_ServerMode)
950                     {
951                         errorCode = SSPIWrapper.AcceptSecurityContext(
952                                         GlobalSSPI.SSPISecureChannel,
953                                         ref m_CredentialsHandle,
954                                         ref m_SecurityContext,
955                                         ServerRequiredFlags | (m_RemoteCertRequired? ContextFlags.MutualAuth: ContextFlags.Zero),
956                                         Endianness.Native,
957                                         incomingSecurity,
958                                         outgoingSecurity,
959                                         ref m_Attributes
960                                         );
961
962                     }
963                     else
964                     {
965                         if(incomingSecurity == null)
966                         {
967                             errorCode = SSPIWrapper.InitializeSecurityContext(
968                                             GlobalSSPI.SSPISecureChannel,
969                                             ref m_CredentialsHandle,
970                                             ref m_SecurityContext,
971                                             m_Destination,
972                                             RequiredFlags | ContextFlags.InitManualCredValidation,
973                                             Endianness.Native,
974                                             incomingSecurity,
975                                             outgoingSecurity,
976                                             ref m_Attributes
977                                             );
978
979                             // This only needs to happen the first time per context.
980                             if ((errorCode == (int)SecurityStatus.OK || errorCode == (int)SecurityStatus.ContinueNeeded) 
981                                 && ComNetOS.IsWin8orLater && Microsoft.Win32.UnsafeNativeMethods.IsPackagedProcess.Value)
982                             {
983                                 // Windows Store app. Specify a window handle in case SChannel needs to pop-up prompts, like 
984                                 // when it asks for permission to use a client certificate.
985                                 int setError = SSPIWrapper.SetContextAttributes(GlobalSSPI.SSPISecureChannel,
986                                                 m_SecurityContext, 
987                                                 ContextAttribute.UiInfo,
988                                                 UnsafeNclNativeMethods.AppXHelper.PrimaryWindowHandle.Value);
989                                 Debug.Assert(setError == 0, "SetContextAttributes error: " + setError);
990                             }
991                         }
992                         else
993                         {
994                             errorCode = SSPIWrapper.InitializeSecurityContext(
995                                             GlobalSSPI.SSPISecureChannel,
996                                             m_CredentialsHandle,
997                                             ref m_SecurityContext,
998                                             m_Destination,
999                                             RequiredFlags | ContextFlags.InitManualCredValidation,
1000                                             Endianness.Native,
1001                                             incomingSecurityBuffers,
1002                                             outgoingSecurity,
1003                                             ref m_Attributes
1004                                             );
1005                         }
1006                     }
1007
1008                 } while (cachedCreds && m_CredentialsHandle == null);
1009             }
1010             finally
1011             {
1012                 if (m_RefreshCredentialNeeded)
1013                 {
1014                     m_RefreshCredentialNeeded = false;
1015                     //
1016                     // Assuming the ISC or ASC has referenced the credential,
1017                     // we want to call dispose so to decrement the effective ref count.
1018                     //
1019                     if (m_CredentialsHandle !=null)
1020                         m_CredentialsHandle.Close();
1021                     //
1022                     // This call may bump up the credential reference count further
1023                     //
1024                     // Note that thumbPrint is retrieved from a safe cert object that was possible cloned from the user passed cert
1025                     //
1026                     if (!cachedCreds && m_SecurityContext != null && !m_SecurityContext.IsInvalid && !m_CredentialsHandle.IsInvalid)
1027                     {
1028                         SslSessionsCache.CacheCredential(m_CredentialsHandle,  thumbPrint, m_ProtocolFlags, m_EncryptionPolicy);
1029                     }
1030                 }
1031
1032             }
1033
1034             output = outgoingSecurity.token;
1035
1036 #if TRAVE
1037             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::GenerateToken()", MapSecurityStatus((uint)errorCode));
1038 #endif
1039             return (SecurityStatus) errorCode;
1040         }
1041
1042         /*++
1043
1044             ProcessHandshakeSuccess -
1045                Called on successful completion of Handshake -
1046                used to set header/trailer sizes for encryption use
1047
1048             Fills in the information about established protocol
1049
1050         --*/
1051         internal void ProcessHandshakeSuccess() {
1052             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::ProcessHandshakeSuccess");
1053             StreamSizes streamSizes = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPISecureChannel, m_SecurityContext, ContextAttribute.StreamSizes) as StreamSizes;
1054             if (streamSizes != null) {
1055                 try
1056                 {
1057                     m_HeaderSize = streamSizes.header;
1058                     m_TrailerSize = streamSizes.trailer;
1059                     m_MaxDataSize = checked(streamSizes.maximumMessage - (m_HeaderSize + m_TrailerSize));
1060                 }
1061                 catch(Exception e)
1062                 {
1063                     if (!NclUtilities.IsFatal(e)){
1064                         GlobalLog.Assert(false, "SecureChannel#" + ValidationHelper.HashString(this) + "::ProcessHandshakeSuccess", "StreamSizes out of range.");
1065                     }
1066                     throw;
1067                 }
1068             }
1069             m_ConnectionInfo = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPISecureChannel, m_SecurityContext, ContextAttribute.ConnectionInfo) as SslConnectionInfo;
1070             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::ProcessHandshakeSuccess");
1071         }
1072
1073         /*++
1074             Encrypt - Encrypts our bytes before we send them over the wire
1075
1076             PERF: make more efficient, this does an extra copy when the offset
1077             is non-zero.
1078
1079             Input:
1080                 buffer - bytes for sending
1081                 offset -
1082                 size   -
1083                 output - Encrypted bytes
1084
1085         --*/
1086
1087
1088         internal SecurityStatus Encrypt(byte[] buffer, int offset, int size, ref byte[] output, out int resultSize) {
1089             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt");
1090             GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt() - offset: " + offset.ToString() + " size: " + size.ToString() +" buffersize: " + buffer.Length.ToString() );
1091             GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt() buffer:[" + Encoding.ASCII.GetString(buffer, 0, Math.Min(buffer.Length,128)) + "]");
1092
1093             byte[] e_writeBuffer;
1094             try
1095             {
1096                 if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length))
1097                 {
1098                     throw new ArgumentOutOfRangeException("offset");
1099                 }
1100                 if (size < 0 || size > (buffer == null ? 0 : buffer.Length - offset))
1101                 {
1102                     throw new ArgumentOutOfRangeException("size");
1103                 }
1104
1105                 resultSize = 0;
1106
1107                 int bufferSizeNeeded = checked(size + m_HeaderSize + m_TrailerSize);
1108                 if (output != null && bufferSizeNeeded <= output.Length)
1109                 {
1110                     e_writeBuffer = output;
1111                 }
1112                 else
1113                 {
1114                     e_writeBuffer = new byte[bufferSizeNeeded];
1115                 }
1116                 Buffer.BlockCopy(buffer,  offset, e_writeBuffer, m_HeaderSize, size);
1117             }
1118             catch(Exception e)
1119             {
1120                 if (!NclUtilities.IsFatal(e)){
1121                     GlobalLog.Assert(false, "SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt", "Arguments out of range.");
1122                 }
1123                 throw;
1124             }
1125
1126             // encryption using SCHANNEL requires 4 buffers: header, payload, trailer, empty
1127
1128             SecurityBuffer[] securityBuffer = new SecurityBuffer[4];
1129
1130             securityBuffer[0] = new SecurityBuffer(e_writeBuffer, 0, m_HeaderSize, BufferType.Header);
1131             securityBuffer[1] = new SecurityBuffer(e_writeBuffer, m_HeaderSize, size, BufferType.Data);
1132             securityBuffer[2] = new SecurityBuffer(e_writeBuffer, m_HeaderSize + size, m_TrailerSize, BufferType.Trailer);
1133             securityBuffer[3] = new SecurityBuffer(null, BufferType.Empty);
1134
1135             int errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPISecureChannel, m_SecurityContext, securityBuffer, 0);
1136
1137             if (errorCode != 0) {
1138                 GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt ERROR", errorCode.ToString("x"));
1139                 return (SecurityStatus)errorCode;
1140             }
1141             else {
1142                 output = e_writeBuffer;
1143                 // The full buffer may not be used
1144                 resultSize = securityBuffer[0].size + securityBuffer[1].size + securityBuffer[2].size;
1145                 GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt OK", "data size:" + resultSize.ToString());
1146                 return SecurityStatus.OK;
1147
1148             }
1149         }
1150
1151         internal SecurityStatus Decrypt(byte[] payload, ref int offset, ref int count) {
1152             GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::Decrypt() - offset: " + offset.ToString() + " size: " + count.ToString() +" buffersize: " + payload.Length.ToString() );
1153
1154             if (offset < 0 || offset > (payload == null ? 0 : payload.Length))
1155             {
1156                 GlobalLog.Assert(false, "SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt", "Argument 'offset' out of range.");
1157                 throw new ArgumentOutOfRangeException("offset");
1158             }
1159             if (count < 0 || count > (payload == null ? 0 : payload.Length - offset))
1160             {
1161                 GlobalLog.Assert(false, "SecureChannel#" + ValidationHelper.HashString(this) + "::Encrypt", "Argument 'count' out of range.");
1162                 throw new ArgumentOutOfRangeException("count");
1163             }
1164
1165             // decryption using SCHANNEL requires four buffers
1166             SecurityBuffer[] decspc = new SecurityBuffer[4];
1167             decspc[0] = new SecurityBuffer(payload, offset, count, BufferType.Data);
1168             decspc[1] = new SecurityBuffer(null, BufferType.Empty);
1169             decspc[2] = new SecurityBuffer(null, BufferType.Empty);
1170             decspc[3] = new SecurityBuffer(null, BufferType.Empty);
1171
1172             SecurityStatus errorCode = (SecurityStatus)SSPIWrapper.DecryptMessage(GlobalSSPI.SSPISecureChannel, m_SecurityContext, decspc, 0);
1173
1174             count = 0;
1175             for (int i = 0; i < decspc.Length; i++) {
1176                 // Sucessfully decoded data and placed it at the following position in the buffer.
1177                 if ((errorCode == SecurityStatus.OK && decspc[i].type == BufferType.Data)
1178                     // or we failed to decode the data, here is the encoded data
1179                     || (errorCode != SecurityStatus.OK && decspc[i].type == BufferType.Extra)) {
1180                     offset = decspc[i].offset;
1181                     count = decspc[i].size;
1182                     break;
1183                 }
1184             }
1185           
1186             return errorCode;
1187         }
1188
1189         /*++
1190
1191             VerifyRemoteCertificate - Validates the content of a Remote Certificate
1192
1193             checkCRL if true, checks the certificate revocation list for validity.
1194             checkCertName, if true checks the CN field of the certificate
1195
1196         --*/
1197
1198         //This method validates a remote certificate.
1199         //SECURITY: The scenario is allowed in semitrust StorePermission is asserted for Chain.Build
1200         //          A user callback has unique signature so it is safe to call it under permisison assert.
1201         //
1202         [StorePermission(SecurityAction.Assert, Unrestricted=true)]
1203         internal bool VerifyRemoteCertificate(RemoteCertValidationCallback remoteCertValidationCallback)
1204         {
1205             GlobalLog.Enter("SecureChannel#" + ValidationHelper.HashString(this) + "::VerifyRemoteCertificate");
1206             SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None;
1207             // we don't catch exceptions in this method, so it's safe for "accepted" be initialized with true
1208             bool success = false;
1209             X509Chain chain = null;
1210             X509Certificate2 remoteCertificateEx = null;
1211
1212             try {
1213                 X509Certificate2Collection remoteCertificateStore;
1214                 remoteCertificateEx = GetRemoteCertificate(out remoteCertificateStore);
1215                 m_IsRemoteCertificateAvailable = remoteCertificateEx != null;
1216
1217                 if (remoteCertificateEx == null)
1218                 {
1219                     GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::VerifyRemoteCertificate (no remote cert)", (!m_RemoteCertRequired).ToString());
1220                     sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable;
1221                 }
1222                 else
1223                 {
1224                     chain = new X509Chain();
1225                     chain.ChainPolicy.RevocationMode = m_CheckCertRevocation? X509RevocationMode.Online : X509RevocationMode.NoCheck;
1226                     chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
1227                     if (remoteCertificateStore != null)
1228                         chain.ChainPolicy.ExtraStore.AddRange(remoteCertificateStore);
1229
1230                     if (!chain.Build(remoteCertificateEx)       // Build failed on handle or on policy
1231                         && chain.ChainContext == IntPtr.Zero)   // Build failed to generate a valid handle
1232                     {
1233                         throw new CryptographicException(Marshal.GetLastWin32Error());
1234                     }
1235
1236                     if (m_CheckCertName)
1237                     {
1238                         unsafe {
1239                             uint status = 0;
1240                             ChainPolicyParameter cppStruct = new ChainPolicyParameter();
1241                             cppStruct.cbSize  = ChainPolicyParameter.StructSize;
1242                             cppStruct.dwFlags = 0;
1243
1244
1245                             SSL_EXTRA_CERT_CHAIN_POLICY_PARA eppStruct = new SSL_EXTRA_CERT_CHAIN_POLICY_PARA(IsServer);
1246                             cppStruct.pvExtraPolicyPara = &eppStruct;
1247
1248                             fixed (char* namePtr = m_HostName) {
1249                                 eppStruct.pwszServerName = namePtr;
1250                                 cppStruct.dwFlags |= (int) (IgnoreCertProblem.none & ~IgnoreCertProblem.invalid_name);
1251
1252                                 SafeFreeCertChain chainContext= new SafeFreeCertChain(chain.ChainContext);
1253                                 status = PolicyWrapper.VerifyChainPolicy(chainContext, ref cppStruct);
1254                                 if ((CertificateProblem) status ==  CertificateProblem.CertCN_NO_MATCH)
1255                                     sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch;
1256                             }
1257                         }
1258                     }
1259
1260                     X509ChainStatus[] chainStatusArray = chain.ChainStatus;
1261                     if (chainStatusArray != null && chainStatusArray.Length != 0)
1262                         sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors;
1263
1264                 }
1265
1266                 if (remoteCertValidationCallback != null) {
1267                     success = remoteCertValidationCallback(m_HostName, remoteCertificateEx, chain, sslPolicyErrors);
1268                 } else {
1269                     if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNotAvailable && !m_RemoteCertRequired)
1270                         success = true;
1271                     else
1272                         success = (sslPolicyErrors == SslPolicyErrors.None);
1273                 }
1274
1275                 if (Logging.On) {
1276                     if (sslPolicyErrors != SslPolicyErrors.None)
1277                     {
1278                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_remote_cert_has_errors));
1279                         if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0)
1280                             Logging.PrintInfo(Logging.Web, this, "\t" + SR.GetString(SR.net_log_remote_cert_not_available));
1281                         if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
1282                             Logging.PrintInfo(Logging.Web, this, "\t" + SR.GetString(SR.net_log_remote_cert_name_mismatch));
1283                         if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
1284                             foreach (X509ChainStatus chainStatus in chain.ChainStatus)
1285                                 Logging.PrintInfo(Logging.Web, this, "\t" + chainStatus.StatusInformation);
1286                     }
1287                     if (success)
1288                     {
1289                         if (remoteCertValidationCallback != null)
1290                             Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_remote_cert_user_declared_valid));
1291                         else
1292                             Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_remote_cert_has_no_errors));
1293                     }
1294                     else
1295                     {
1296                         if (remoteCertValidationCallback != null)
1297                             Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_remote_cert_user_declared_invalid));
1298                     }
1299                 }
1300                 GlobalLog.Print("Cert Validation, remote cert = " + (remoteCertificateEx == null? "<null>": remoteCertificateEx.ToString(true)));
1301             }
1302             finally {
1303                 // At least on Win2k server the chain is found to have dependancies on the original cert context.
1304                 // So it should be closed first.
1305                 if (chain != null) {
1306                     chain.Reset();
1307                 }
1308                 if (remoteCertificateEx != null)
1309                     remoteCertificateEx.Reset();
1310             }
1311             GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::VerifyRemoteCertificate", success.ToString());
1312             return success;
1313         }
1314
1315         /*
1316             From wincrypt.h
1317
1318         typedef void *HCERTSTORE;
1319
1320         //+-------------------------------------------------------------------------
1321         //  Certificate context.
1322         //
1323         //  A certificate context contains both the encoded and decoded representation
1324         //  of a certificate. A certificate context returned by a cert store function
1325         //  must be freed by calling the CertFreeCertificateContext function. The
1326         //  CertDuplicateCertificateContext function can be called to make a duplicate
1327         //  copy (which also must be freed by calling CertFreeCertificateContext).
1328         //--------------------------------------------------------------------------
1329         */
1330         /*
1331         // Consider removing.
1332         unsafe class CertificateContext {
1333
1334             [StructLayout(LayoutKind.Sequential)]
1335             private struct _CERT_CONTEXT {
1336                 internal Int32     dwCertEncodingType;
1337                 internal IntPtr    pbCertEncoded;
1338                 internal Int32     cbCertEncoded;
1339                 internal IntPtr    pCertInfo;
1340                 internal   IntPtr    hCertStore;
1341             };
1342
1343             internal static SafeCloseStore GetShallowHStoreHandler(SafeFreeCertContext certContext) {
1344                 if (certContext.IsInvalid) {
1345                     return SafeCloseStore.CreateShallowHandle(IntPtr.Zero);
1346                 }
1347                 _CERT_CONTEXT context = (_CERT_CONTEXT)Marshal.PtrToStructure(certContext.DangerousGetHandle(), typeof(_CERT_CONTEXT));
1348                 return SafeCloseStore.CreateShallowHandle(context.hCertStore);
1349             }
1350
1351
1352 #if DEBUG
1353             _CERT_CONTEXT dbgTemplate;
1354
1355             // ctors
1356             internal CertificateContext(SafeFreeCertContext context) {
1357                 GlobalLog.Enter("CertificateContext#" + ValidationHelper.HashString(this) + "::CertificateContext", context.DangerousGetHandle().ToString("x"));
1358                 dbgTemplate = (_CERT_CONTEXT)Marshal.PtrToStructure(context.DangerousGetHandle(), typeof(_CERT_CONTEXT));
1359                 GlobalLog.Leave("CertificateContext#" + ValidationHelper.HashString(this) + "::CertificateContext");
1360             }
1361
1362             // methods
1363             [Conditional("TRAVE")]
1364             internal void DebugDump() {
1365                 GlobalLog.Print("CertificateContext#" + ValidationHelper.HashString(this) + "::CertificateContext()");
1366                 GlobalLog.PrintHex("    dwCertEncodingType = ", dbgTemplate.dwCertEncodingType);
1367                 GlobalLog.PrintHex("    pbCertEncoded      = ", dbgTemplate.pbCertEncoded);
1368                 GlobalLog.PrintHex("    cbCertEncoded      = ", dbgTemplate.cbCertEncoded);
1369                 GlobalLog.PrintHex("    pCertInfo          = ", dbgTemplate.pCertInfo);
1370                 GlobalLog.PrintHex("    hCertStore         = ", dbgTemplate.hCertStore);
1371             }
1372 #endif
1373         }
1374         */
1375
1376 #if TRAVE
1377         internal static string MapSecurityStatus(uint statusCode) {
1378             switch (statusCode) {
1379             case 0: return "0";
1380             case 0x80090001: return "NTE_BAD_UID";
1381             case 0x80090002: return "NTE_BAD_HASH";
1382             case 0x80090003: return "NTE_BAD_KEY";
1383             case 0x80090004: return "NTE_BAD_LEN";
1384             case 0x80090005: return "NTE_BAD_DATA";
1385             case 0x80090006: return "NTE_BAD_SIGNATURE";
1386             case 0x80090007: return "NTE_BAD_VER";
1387             case 0x80090008: return "NTE_BAD_ALGID";
1388             case 0x80090009: return "NTE_BAD_FLAGS";
1389             case 0x8009000A: return "NTE_BAD_TYPE";
1390             case 0x8009000B: return "NTE_BAD_KEY_STATE";
1391             case 0x8009000C: return "NTE_BAD_HASH_STATE";
1392             case 0x8009000D: return "NTE_NO_KEY";
1393             case 0x8009000E: return "NTE_NO_MEMORY";
1394             case 0x8009000F: return "NTE_EXISTS";
1395             case 0x80090010: return "NTE_PERM";
1396             case 0x80090011: return "NTE_NOT_FOUND";
1397             case 0x80090012: return "NTE_DOUBLE_ENCRYPT";
1398             case 0x80090013: return "NTE_BAD_PROVIDER";
1399             case 0x80090014: return "NTE_BAD_PROV_TYPE";
1400             case 0x80090015: return "NTE_BAD_PUBLIC_KEY";
1401             case 0x80090016: return "NTE_BAD_KEYSET";
1402             case 0x80090017: return "NTE_PROV_TYPE_NOT_DEF";
1403             case 0x80090018: return "NTE_PROV_TYPE_ENTRY_BAD";
1404             case 0x80090019: return "NTE_KEYSET_NOT_DEF";
1405             case 0x8009001A: return "NTE_KEYSET_ENTRY_BAD";
1406             case 0x8009001B: return "NTE_PROV_TYPE_NO_MATCH";
1407             case 0x8009001C: return "NTE_SIGNATURE_FILE_BAD";
1408             case 0x8009001D: return "NTE_PROVIDER_DLL_FAIL";
1409             case 0x8009001E: return "NTE_PROV_DLL_NOT_FOUND";
1410             case 0x8009001F: return "NTE_BAD_KEYSET_PARAM";
1411             case 0x80090020: return "NTE_FAIL";
1412             case 0x80090021: return "NTE_SYS_ERR";
1413             case 0x80090022: return "NTE_SILENT_CONTEXT";
1414             case 0x80090023: return "NTE_TOKEN_KEYSET_STORAGE_FULL";
1415             case 0x80090024: return "NTE_TEMPORARY_PROFILE";
1416             case 0x80090025: return "NTE_FIXEDPARAMETER";
1417             case 0x80090300: return "SEC_E_INSUFFICIENT_MEMORY";
1418             case 0x80090301: return "SEC_E_INVALID_HANDLE";
1419             case 0x80090302: return "SEC_E_UNSUPPORTED_FUNCTION";
1420             case 0x80090303: return "SEC_E_TARGET_UNKNOWN";
1421             case 0x80090304: return "SEC_E_INTERNAL_ERROR";
1422             case 0x80090305: return "SEC_E_SECPKG_NOT_FOUND";
1423             case 0x80090306: return "SEC_E_NOT_OWNER";
1424             case 0x80090307: return "SEC_E_CANNOT_INSTALL";
1425             case 0x80090308: return "SEC_E_INVALID_TOKEN";
1426             case 0x80090309: return "SEC_E_CANNOT_PACK";
1427             case 0x8009030A: return "SEC_E_QOP_NOT_SUPPORTED";
1428             case 0x8009030B: return "SEC_E_NO_IMPERSONATION";
1429             case 0x8009030C: return "SEC_E_LOGON_DENIED";
1430             case 0x8009030D: return "SEC_E_UNKNOWN_CREDENTIALS";
1431             case 0x8009030E: return "SEC_E_NO_CREDENTIALS";
1432             case 0x8009030F: return "SEC_E_MESSAGE_ALTERED";
1433             case 0x80090310: return "SEC_E_OUT_OF_SEQUENCE";
1434             case 0x80090311: return "SEC_E_NO_AUTHENTICATING_AUTHORITY";
1435             case 0x00090312: return "SEC_I_CONTINUE_NEEDED";
1436             case 0x00090313: return "SEC_I_COMPLETE_NEEDED";
1437             case 0x00090314: return "SEC_I_COMPLETE_AND_CONTINUE";
1438             case 0x00090315: return "SEC_I_LOCAL_LOGON";
1439             case 0x80090316: return "SEC_E_BAD_PKGID";
1440             case 0x80090317: return "SEC_E_CONTEXT_EXPIRED";
1441             case 0x00090317: return "SEC_I_CONTEXT_EXPIRED";
1442             case 0x80090318: return "SEC_E_INCOMPLETE_MESSAGE";
1443             case 0x80090320: return "SEC_E_INCOMPLETE_CREDENTIALS";
1444             case 0x80090321: return "SEC_E_BUFFER_TOO_SMALL";
1445             case 0x00090320: return "SEC_I_INCOMPLETE_CREDENTIALS";
1446             case 0x00090321: return "SEC_I_RENEGOTIATE";
1447             case 0x80090322: return "SEC_E_WRONG_PRINCIPAL";
1448             case 0x00090323: return "SEC_I_NO_LSA_CONTEXT";
1449             case 0x80090324: return "SEC_E_TIME_SKEW";
1450             case 0x80090325: return "SEC_E_UNTRUSTED_ROOT";
1451             case 0x80090326: return "SEC_E_ILLEGAL_MESSAGE";
1452             case 0x80090327: return "SEC_E_CERT_UNKNOWN";
1453             case 0x80090328: return "SEC_E_CERT_EXPIRED";
1454             case 0x80090329: return "SEC_E_ENCRYPT_FAILURE";
1455             case 0x80090330: return "SEC_E_DECRYPT_FAILURE";
1456             case 0x80090331: return "SEC_E_ALGORITHM_MISMATCH";
1457             case 0x80090332: return "SEC_E_SECURITY_QOS_FAILED";
1458             case 0x80090333: return "SEC_E_UNFINISHED_CONTEXT_DELETED";
1459             case 0x80090334: return "SEC_E_NO_TGT_REPLY";
1460             case 0x80090335: return "SEC_E_NO_IP_ADDRESSES";
1461             case 0x80090336: return "SEC_E_WRONG_CREDENTIAL_HANDLE";
1462             case 0x80090337: return "SEC_E_CRYPTO_SYSTEM_INVALID";
1463             case 0x80090338: return "SEC_E_MAX_REFERRALS_EXCEEDED";
1464             case 0x80090339: return "SEC_E_MUST_BE_KDC";
1465             case 0x8009033A: return "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED";
1466             case 0x8009033B: return "SEC_E_TOO_MANY_PRINCIPALS";
1467             case 0x8009033C: return "SEC_E_NO_PA_DATA";
1468             case 0x8009033D: return "SEC_E_PKINIT_NAME_MISMATCH";
1469             case 0x8009033E: return "SEC_E_SMARTCARD_LOGON_REQUIRED";
1470             case 0x8009033F: return "SEC_E_SHUTDOWN_IN_PROGRESS";
1471             case 0x80090340: return "SEC_E_KDC_INVALID_REQUEST";
1472             case 0x80090341: return "SEC_E_KDC_UNABLE_TO_REFER";
1473             case 0x80090342: return "SEC_E_KDC_UNKNOWN_ETYPE";
1474             case 0x80090343: return "SEC_E_UNSUPPORTED_PREAUTH";
1475             case 0x80090345: return "SEC_E_DELEGATION_REQUIRED";
1476             case 0x80090346: return "SEC_E_BAD_BINDINGS";
1477             case 0x80090347: return "SEC_E_MULTIPLE_ACCOUNTS";
1478             case 0x80090348: return "SEC_E_NO_KERB_KEY";
1479             case 0x80091001: return "CRYPT_E_MSG_ERROR";
1480             case 0x80091002: return "CRYPT_E_UNKNOWN_ALGO";
1481             case 0x80091003: return "CRYPT_E_OID_FORMAT";
1482             case 0x80091004: return "CRYPT_E_INVALID_MSG_TYPE";
1483             case 0x80091005: return "CRYPT_E_UNEXPECTED_ENCODING";
1484             case 0x80091006: return "CRYPT_E_AUTH_ATTR_MISSING";
1485             case 0x80091007: return "CRYPT_E_HASH_VALUE";
1486             case 0x80091008: return "CRYPT_E_INVALID_INDEX";
1487             case 0x80091009: return "CRYPT_E_ALREADY_DECRYPTED";
1488             case 0x8009100A: return "CRYPT_E_NOT_DECRYPTED";
1489             case 0x8009100B: return "CRYPT_E_RECIPIENT_NOT_FOUND";
1490             case 0x8009100C: return "CRYPT_E_CONTROL_TYPE";
1491             case 0x8009100D: return "CRYPT_E_ISSUER_SERIALNUMBER";
1492             case 0x8009100E: return "CRYPT_E_SIGNER_NOT_FOUND";
1493             case 0x8009100F: return "CRYPT_E_ATTRIBUTES_MISSING";
1494             case 0x80091010: return "CRYPT_E_STREAM_MSG_NOT_READY";
1495             case 0x80091011: return "CRYPT_E_STREAM_INSUFFICIENT_DATA";
1496             case 0x00091012: return "CRYPT_I_NEW_PROTECTION_REQUIRED";
1497             case 0x80092001: return "CRYPT_E_BAD_LEN";
1498             case 0x80092002: return "CRYPT_E_BAD_ENCODE";
1499             case 0x80092003: return "CRYPT_E_FILE_ERROR";
1500             case 0x80092004: return "CRYPT_E_NOT_FOUND";
1501             case 0x80092005: return "CRYPT_E_EXISTS";
1502             case 0x80092006: return "CRYPT_E_NO_PROVIDER";
1503             case 0x80092007: return "CRYPT_E_SELF_SIGNED";
1504             case 0x80092008: return "CRYPT_E_DELETED_PREV";
1505             case 0x80092009: return "CRYPT_E_NO_MATCH";
1506             case 0x8009200A: return "CRYPT_E_UNEXPECTED_MSG_TYPE";
1507             case 0x8009200B: return "CRYPT_E_NO_KEY_PROPERTY";
1508             case 0x8009200C: return "CRYPT_E_NO_DECRYPT_CERT";
1509             case 0x8009200D: return "CRYPT_E_BAD_MSG";
1510             case 0x8009200E: return "CRYPT_E_NO_SIGNER";
1511             case 0x8009200F: return "CRYPT_E_PENDING_CLOSE";
1512             case 0x80092010: return "CRYPT_E_REVOKED";
1513             case 0x80092011: return "CRYPT_E_NO_REVOCATION_DLL";
1514             case 0x80092012: return "CRYPT_E_NO_REVOCATION_CHECK";
1515             case 0x80092013: return "CRYPT_E_REVOCATION_OFFLINE";
1516             case 0x80092014: return "CRYPT_E_NOT_IN_REVOCATION_DATABASE";
1517             case 0x80092020: return "CRYPT_E_INVALID_NUMERIC_STRING";
1518             case 0x80092021: return "CRYPT_E_INVALID_PRINTABLE_STRING";
1519             case 0x80092022: return "CRYPT_E_INVALID_IA5_STRING";
1520             case 0x80092023: return "CRYPT_E_INVALID_X500_STRING";
1521             case 0x80092024: return "CRYPT_E_NOT_CHAR_STRING";
1522             case 0x80092025: return "CRYPT_E_FILERESIZED";
1523             case 0x80092026: return "CRYPT_E_SECURITY_SETTINGS";
1524             case 0x80092027: return "CRYPT_E_NO_VERIFY_USAGE_DLL";
1525             case 0x80092028: return "CRYPT_E_NO_VERIFY_USAGE_CHECK";
1526             case 0x80092029: return "CRYPT_E_VERIFY_USAGE_OFFLINE";
1527             case 0x8009202A: return "CRYPT_E_NOT_IN_CTL";
1528             case 0x8009202B: return "CRYPT_E_NO_TRUSTED_SIGNER";
1529             case 0x8009202C: return "CRYPT_E_MISSING_PUBKEY_PARA";
1530             case 0x80093000: return "CRYPT_E_OSS_ERROR";
1531             case 0x80093001: return "OSS_MORE_BUF";
1532             case 0x80093002: return "OSS_NEGATIVE_UINTEGER";
1533             case 0x80093003: return "OSS_PDU_RANGE";
1534             case 0x80093004: return "OSS_MORE_INPUT";
1535             case 0x80093005: return "OSS_DATA_ERROR";
1536             case 0x80093006: return "OSS_BAD_ARG";
1537             case 0x80093007: return "OSS_BAD_VERSION";
1538             case 0x80093008: return "OSS_OUT_MEMORY";
1539             case 0x80093009: return "OSS_PDU_MISMATCH";
1540             case 0x8009300A: return "OSS_LIMITED";
1541             case 0x8009300B: return "OSS_BAD_PTR";
1542             case 0x8009300C: return "OSS_BAD_TIME";
1543             case 0x8009300D: return "OSS_INDEFINITE_NOT_SUPPORTED";
1544             case 0x8009300E: return "OSS_MEM_ERROR";
1545             case 0x8009300F: return "OSS_BAD_TABLE";
1546             case 0x80093010: return "OSS_TOO_LONG";
1547             case 0x80093011: return "OSS_CONSTRAINT_VIOLATED";
1548             case 0x80093012: return "OSS_FATAL_ERROR";
1549             case 0x80093013: return "OSS_ACCESS_SERIALIZATION_ERROR";
1550             case 0x80093014: return "OSS_NULL_TBL";
1551             case 0x80093015: return "OSS_NULL_FCN";
1552             case 0x80093016: return "OSS_BAD_ENCRULES";
1553             case 0x80093017: return "OSS_UNAVAIL_ENCRULES";
1554             case 0x80093018: return "OSS_CANT_OPEN_TRACE_WINDOW";
1555             case 0x80093019: return "OSS_UNIMPLEMENTED";
1556             case 0x8009301A: return "OSS_OID_DLL_NOT_LINKED";
1557             case 0x8009301B: return "OSS_CANT_OPEN_TRACE_FILE";
1558             case 0x8009301C: return "OSS_TRACE_FILE_ALREADY_OPEN";
1559             case 0x8009301D: return "OSS_TABLE_MISMATCH";
1560             case 0x8009301E: return "OSS_TYPE_NOT_SUPPORTED";
1561             case 0x8009301F: return "OSS_REAL_DLL_NOT_LINKED";
1562             case 0x80093020: return "OSS_REAL_CODE_NOT_LINKED";
1563             case 0x80093021: return "OSS_OUT_OF_RANGE";
1564             case 0x80093022: return "OSS_COPIER_DLL_NOT_LINKED";
1565             case 0x80093023: return "OSS_CONSTRAINT_DLL_NOT_LINKED";
1566             case 0x80093024: return "OSS_COMPARATOR_DLL_NOT_LINKED";
1567             case 0x80093025: return "OSS_COMPARATOR_CODE_NOT_LINKED";
1568             case 0x80093026: return "OSS_MEM_MGR_DLL_NOT_LINKED";
1569             case 0x80093027: return "OSS_PDV_DLL_NOT_LINKED";
1570             case 0x80093028: return "OSS_PDV_CODE_NOT_LINKED";
1571             case 0x80093029: return "OSS_API_DLL_NOT_LINKED";
1572             case 0x8009302A: return "OSS_BERDER_DLL_NOT_LINKED";
1573             case 0x8009302B: return "OSS_PER_DLL_NOT_LINKED";
1574             case 0x8009302C: return "OSS_OPEN_TYPE_ERROR";
1575             case 0x8009302D: return "OSS_MUTEX_NOT_CREATED";
1576             case 0x8009302E: return "OSS_CANT_CLOSE_TRACE_FILE";
1577             case 0x80093100: return "CRYPT_E_ASN1_ERROR";
1578             case 0x80093101: return "CRYPT_E_ASN1_INTERNAL";
1579             case 0x80093102: return "CRYPT_E_ASN1_EOD";
1580             case 0x80093103: return "CRYPT_E_ASN1_CORRUPT";
1581             case 0x80093104: return "CRYPT_E_ASN1_LARGE";
1582             case 0x80093105: return "CRYPT_E_ASN1_CONSTRAINT";
1583             case 0x80093106: return "CRYPT_E_ASN1_MEMORY";
1584             case 0x80093107: return "CRYPT_E_ASN1_OVERFLOW";
1585             case 0x80093108: return "CRYPT_E_ASN1_BADPDU";
1586             case 0x80093109: return "CRYPT_E_ASN1_BADARGS";
1587             case 0x8009310A: return "CRYPT_E_ASN1_BADREAL";
1588             case 0x8009310B: return "CRYPT_E_ASN1_BADTAG";
1589             case 0x8009310C: return "CRYPT_E_ASN1_CHOICE";
1590             case 0x8009310D: return "CRYPT_E_ASN1_RULE";
1591             case 0x8009310E: return "CRYPT_E_ASN1_UTF8";
1592             case 0x80093133: return "CRYPT_E_ASN1_PDU_TYPE";
1593             case 0x80093134: return "CRYPT_E_ASN1_NYI";
1594             case 0x80093201: return "CRYPT_E_ASN1_EXTENDED";
1595             case 0x80093202: return "CRYPT_E_ASN1_NOEOD";
1596             case 0x80094001: return "CERTSRV_E_BAD_REQUESTSUBJECT";
1597             case 0x80094002: return "CERTSRV_E_NO_REQUEST";
1598             case 0x80094003: return "CERTSRV_E_BAD_REQUESTSTATUS";
1599             case 0x80094004: return "CERTSRV_E_PROPERTY_EMPTY";
1600             case 0x80094005: return "CERTSRV_E_INVALID_CA_CERTIFICATE";
1601             case 0x80094006: return "CERTSRV_E_SERVER_SUSPENDED";
1602             case 0x80094007: return "CERTSRV_E_ENCODING_LENGTH";
1603             case 0x80094008: return "CERTSRV_E_ROLECONFLICT";
1604             case 0x80094009: return "CERTSRV_E_RESTRICTEDOFFICER";
1605             case 0x8009400A: return "CERTSRV_E_KEY_ARCHIVAL_NOT_CONFIGURED";
1606             case 0x8009400B: return "CERTSRV_E_NO_VALID_KRA";
1607             case 0x8009400C: return "CERTSRV_E_BAD_REQUEST_KEY_ARCHIVAL";
1608             case 0x80094800: return "CERTSRV_E_UNSUPPORTED_CERT_TYPE";
1609             case 0x80094801: return "CERTSRV_E_NO_CERT_TYPE";
1610             case 0x80094802: return "CERTSRV_E_TEMPLATE_CONFLICT";
1611             case 0x80096001: return "TRUST_E_SYSTEM_ERROR";
1612             case 0x80096002: return "TRUST_E_NO_SIGNER_CERT";
1613             case 0x80096003: return "TRUST_E_COUNTER_SIGNER";
1614             case 0x80096004: return "TRUST_E_CERT_SIGNATURE";
1615             case 0x80096005: return "TRUST_E_TIME_STAMP";
1616             case 0x80096010: return "TRUST_E_BAD_DIGEST";
1617             case 0x80096019: return "TRUST_E_BASIC_CONSTRAINTS";
1618             case 0x8009601E: return "TRUST_E_FINANCIAL_CRITERIA";
1619             case 0x80097001: return "MSSIPOTF_E_OUTOFMEMRANGE";
1620             case 0x80097002: return "MSSIPOTF_E_CANTGETOBJECT";
1621             case 0x80097003: return "MSSIPOTF_E_NOHEADTABLE";
1622             case 0x80097004: return "MSSIPOTF_E_BAD_MAGICNUMBER";
1623             case 0x80097005: return "MSSIPOTF_E_BAD_OFFSET_TABLE";
1624             case 0x80097006: return "MSSIPOTF_E_TABLE_TAGORDER";
1625             case 0x80097007: return "MSSIPOTF_E_TABLE_LONGWORD";
1626             case 0x80097008: return "MSSIPOTF_E_BAD_FIRST_TABLE_PLACEMENT";
1627             case 0x80097009: return "MSSIPOTF_E_TABLES_OVERLAP";
1628             case 0x8009700A: return "MSSIPOTF_E_TABLE_PADBYTES";
1629             case 0x8009700B: return "MSSIPOTF_E_FILETOOSMALL";
1630             case 0x8009700C: return "MSSIPOTF_E_TABLE_CHECKSUM";
1631             case 0x8009700D: return "MSSIPOTF_E_FILE_CHECKSUM";
1632             case 0x80097010: return "MSSIPOTF_E_FAILED_POLICY";
1633             case 0x80097011: return "MSSIPOTF_E_FAILED_HINTS_CHECK";
1634             case 0x80097012: return "MSSIPOTF_E_NOT_OPENTYPE";
1635             case 0x80097013: return "MSSIPOTF_E_FILE";
1636             case 0x80097014: return "MSSIPOTF_E_CRYPT";
1637             case 0x80097015: return "MSSIPOTF_E_BADVERSION";
1638             case 0x80097016: return "MSSIPOTF_E_DSIG_STRUCTURE";
1639             case 0x80097017: return "MSSIPOTF_E_PCONST_CHECK";
1640             case 0x80097018: return "MSSIPOTF_E_STRUCTURE";
1641             case 0x800B0001: return "TRUST_E_PROVIDER_UNKNOWN";
1642             case 0x800B0002: return "TRUST_E_ACTION_UNKNOWN";
1643             case 0x800B0003: return "TRUST_E_SUBJECT_FORM_UNKNOWN";
1644             case 0x800B0004: return "TRUST_E_SUBJECT_NOT_TRUSTED";
1645             case 0x800B0005: return "DIGSIG_E_ENCODE";
1646             case 0x800B0006: return "DIGSIG_E_DECODE";
1647             case 0x800B0007: return "DIGSIG_E_EXTENSIBILITY";
1648             case 0x800B0008: return "DIGSIG_E_CRYPTO";
1649             case 0x800B0009: return "PERSIST_E_SIZEDEFINITE";
1650             case 0x800B000A: return "PERSIST_E_SIZEINDEFINITE";
1651             case 0x800B000B: return "PERSIST_E_NOTSELFSIZING";
1652             case 0x800B0100: return "TRUST_E_NOSIGNATURE";
1653             case 0x800B0101: return "CERT_E_EXPIRED";
1654             case 0x800B0102: return "CERT_E_VALIDITYPERIODNESTING";
1655             case 0x800B0103: return "CERT_E_ROLE";
1656             case 0x800B0104: return "CERT_E_PATHLENCONST";
1657             case 0x800B0105: return "CERT_E_CRITICAL";
1658             case 0x800B0106: return "CERT_E_PURPOSE";
1659             case 0x800B0107: return "CERT_E_ISSUERCHAINING";
1660             case 0x800B0108: return "CERT_E_MALFORMED";
1661             case 0x800B0109: return "CERT_E_UNTRUSTEDROOT";
1662             case 0x800B010A: return "CERT_E_CHAINING";
1663             case 0x800B010B: return "TRUST_E_FAIL";
1664             case 0x800B010C: return "CERT_E_REVOKED";
1665             case 0x800B010D: return "CERT_E_UNTRUSTEDTESTROOT";
1666             case 0x800B010E: return "CERT_E_REVOCATION_FAILURE";
1667             case 0x800B010F: return "CERT_E_CN_NO_MATCH";
1668             case 0x800B0110: return "CERT_E_WRONG_USAGE";
1669             case 0x800B0111: return "TRUST_E_EXPLICIT_DISTRUST";
1670             case 0x800B0112: return "CERT_E_UNTRUSTEDCA";
1671             case 0x800B0113: return "CERT_E_INVALID_POLICY";
1672             case 0x800B0114: return "CERT_E_INVALID_NAME";
1673             }
1674             return String.Format("0x{0:x} [{1}]", statusCode, statusCode);
1675         }
1676
1677         static readonly string[] InputContextAttributes = {
1678             "ISC_REQ_DELEGATE",                 // 0x00000001
1679             "ISC_REQ_MUTUAL_AUTH",              // 0x00000002
1680             "ISC_REQ_REPLAY_DETECT",            // 0x00000004
1681             "ISC_REQ_SEQUENCE_DETECT",          // 0x00000008
1682             "ISC_REQ_CONFIDENTIALITY",          // 0x00000010
1683             "ISC_REQ_USE_SESSION_KEY",          // 0x00000020
1684             "ISC_REQ_PROMPT_FOR_CREDS",         // 0x00000040
1685             "ISC_REQ_USE_SUPPLIED_CREDS",       // 0x00000080
1686             "ISC_REQ_ALLOCATE_MEMORY",          // 0x00000100
1687             "ISC_REQ_USE_DCE_STYLE",            // 0x00000200
1688             "ISC_REQ_DATAGRAM",                 // 0x00000400
1689             "ISC_REQ_CONNECTION",               // 0x00000800
1690             "ISC_REQ_CALL_LEVEL",               // 0x00001000
1691             "ISC_REQ_FRAGMENT_SUPPLIED",        // 0x00002000
1692             "ISC_REQ_EXTENDED_ERROR",           // 0x00004000
1693             "ISC_REQ_STREAM",                   // 0x00008000
1694             "ISC_REQ_INTEGRITY",                // 0x00010000
1695             "ISC_REQ_IDENTIFY",                 // 0x00020000
1696             "ISC_REQ_NULL_SESSION",             // 0x00040000
1697             "ISC_REQ_MANUAL_CRED_VALIDATION",   // 0x00080000
1698             "ISC_REQ_RESERVED1",                // 0x00100000
1699             "ISC_REQ_FRAGMENT_TO_FIT",          // 0x00200000
1700             "?",                                // 0x00400000
1701             "?",                                // 0x00800000
1702             "?",                                // 0x01000000
1703             "?",                                // 0x02000000
1704             "?",                                // 0x04000000
1705             "?",                                // 0x08000000
1706             "?",                                // 0x10000000
1707             "?",                                // 0x20000000
1708             "?",                                // 0x40000000
1709             "?"                                 // 0x80000000
1710         };
1711
1712         static readonly string[] OutputContextAttributes = {
1713             "ISC_RET_DELEGATE",                 // 0x00000001
1714             "ISC_RET_MUTUAL_AUTH",              // 0x00000002
1715             "ISC_RET_REPLAY_DETECT",            // 0x00000004
1716             "ISC_RET_SEQUENCE_DETECT",          // 0x00000008
1717             "ISC_RET_CONFIDENTIALITY",          // 0x00000010
1718             "ISC_RET_USE_SESSION_KEY",          // 0x00000020
1719             "ISC_RET_USED_COLLECTED_CREDS",     // 0x00000040
1720             "ISC_RET_USED_SUPPLIED_CREDS",      // 0x00000080
1721             "ISC_RET_ALLOCATED_MEMORY",         // 0x00000100
1722             "ISC_RET_USED_DCE_STYLE",           // 0x00000200
1723             "ISC_RET_DATAGRAM",                 // 0x00000400
1724             "ISC_RET_CONNECTION",               // 0x00000800
1725             "ISC_RET_INTERMEDIATE_RETURN",      // 0x00001000
1726             "ISC_RET_CALL_LEVEL",               // 0x00002000
1727             "ISC_RET_EXTENDED_ERROR",           // 0x00004000
1728             "ISC_RET_STREAM",                   // 0x00008000
1729             "ISC_RET_INTEGRITY",                // 0x00010000
1730             "ISC_RET_IDENTIFY",                 // 0x00020000
1731             "ISC_RET_NULL_SESSION",             // 0x00040000
1732             "ISC_RET_MANUAL_CRED_VALIDATION",   // 0x00080000
1733             "ISC_RET_RESERVED1",                // 0x00100000
1734             "ISC_RET_FRAGMENT_ONLY",            // 0x00200000
1735             "?",                                // 0x00400000
1736             "?",                                // 0x00800000
1737             "?",                                // 0x01000000
1738             "?",                                // 0x02000000
1739             "?",                                // 0x04000000
1740             "?",                                // 0x08000000
1741             "?",                                // 0x10000000
1742             "?",                                // 0x20000000
1743             "?",                                // 0x40000000
1744             "?"                                 // 0x80000000
1745         };
1746
1747         /*
1748         // Consider removing.
1749         internal static string MapInputContextAttributes(int attributes) {
1750             return ContextAttributeMapper(attributes, InputContextAttributes);
1751         }
1752
1753         internal static string MapOutputContextAttributes(int attributes) {
1754             return ContextAttributeMapper(attributes, OutputContextAttributes);
1755         }
1756
1757         internal static string ContextAttributeMapper(int attributes, string[] attributeNames)        {
1758
1759             int bit = 1;
1760             int index = 0;
1761             string result = "";
1762             bool haveResult = false;
1763
1764             while (attributes != 0) {
1765                 if ((attributes & bit) != 0) {
1766                     if (haveResult) {
1767                         result += " ";
1768                     }
1769                     haveResult = true;
1770                     result += attributeNames[index];
1771                 }
1772                 attributes &= ~bit;
1773                 bit <<= 1;
1774                 ++index;
1775             }
1776             return result;
1777         }
1778         */
1779
1780         [System.Diagnostics.Conditional("DEBUG")]
1781         internal void DebugMembers() {
1782             GlobalLog.Print("m_Destination        =" + m_Destination);
1783             GlobalLog.Print("m_ClientCertificates =" + m_ClientCertificates);
1784             GlobalLog.Print("m_ServerCertificate  =" + m_ServerCertificate);
1785             GlobalLog.Print("m_Attributes         =" + m_Attributes);
1786         }
1787 #endif // TRAVE
1788     }
1789
1790     //
1791     // ProtocolToken - used to process and handle the return codes
1792     //   from the SSPI wrapper
1793     //
1794
1795     class ProtocolToken {
1796         internal SecurityStatus Status;
1797         internal byte[] Payload;
1798         internal int Size;
1799
1800         internal bool Failed {
1801             get {
1802                 return ((Status != SecurityStatus.OK) && (Status != SecurityStatus.ContinueNeeded));
1803             }
1804         }
1805
1806         internal bool Done {
1807             get {
1808                 return (Status == SecurityStatus.OK);
1809             }
1810         }
1811
1812         internal bool Renegotiate {
1813             get {
1814                 return (Status == SecurityStatus.Renegotiate);
1815             }
1816         }
1817
1818         internal bool CloseConnection {
1819             get {
1820                 return (Status == SecurityStatus.ContextExpired);
1821             }
1822         }
1823
1824         internal ProtocolToken(byte[] data, SecurityStatus errorCode) {
1825             Status = errorCode;
1826             Payload = data;
1827             Size = data!=null ? data.Length : 0;
1828         }
1829
1830         internal Win32Exception GetException() {
1831             // if it's not done, then there's got to be an error, even if it's
1832             // a Handshake message up, and we only have a Warning message.
1833             return this.Done ? null : new Win32Exception((int)Status);
1834         }
1835
1836 #if TRAVE
1837         public override string ToString() {
1838             return "Status="+Status.ToString() + ", data size="+Size;
1839         }
1840 #endif
1841
1842     }
1843
1844 }
1845