The Labs \ Source Viewer \ SSCLI \ System.Net.Security \ SslCredKey

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_SslSessionsCache.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. /*++   
  16. Abstract:
  17.     The file implements SSL session caching mechanism based on a static table of SSL credentials
  18. Author:
  19.     Alexei Vopilov    20-Jul-2004
  20. Revision History:
  21. --*/
  22. namespace System.Net.Security
  23. {
  24.    
  25.     using System.Net;
  26.     using System.Security.Cryptography.X509Certificates;
  27.     using System.Collections;
  28.    
  29.     static internal class SslSessionsCache
  30.     {
  31.        
  32.         private const int c_CheckExpiredModulo = 32;
  33.         private static Hashtable s_CachedCreds = new Hashtable(32);
  34.        
  35.         //
  36.         // Uses cryptographically strong certificate thumbprint comparison
  37.         //
  38.         private struct SslCredKey
  39.         {
  40.             private static readonly byte[] s_EmptyArray = new byte[0];
  41.            
  42.             private byte[] _CertThumbPrint;
  43.             private SchProtocols _AllowedProtocols;
  44.             private int _HashCode;
  45.             //
  46.             // SECURITY: X509Certificate.GetCertHash() is virtual hence before going here
  47.             // the caller of this ctor has to ensure that a user cert object was inspected and
  48.             // optionally cloned.
  49.             //
  50.             internal SslCredKey(byte[] thumbPrint, SchProtocols allowedProtocols)
  51.             {
  52.                 _CertThumbPrint = thumbPrint == null ? s_EmptyArray : thumbPrint;
  53.                 _HashCode = 0;
  54.                 if (thumbPrint != null) {
  55.                     _HashCode ^= _CertThumbPrint[0];
  56.                     if (1 < _CertThumbPrint.Length)
  57.                         _HashCode ^= (_CertThumbPrint[1] << 8);
  58.                     if (2 < _CertThumbPrint.Length)
  59.                         _HashCode ^= (_CertThumbPrint[2] << 16);
  60.                     if (3 < _CertThumbPrint.Length)
  61.                         _HashCode ^= (_CertThumbPrint[3] << 24);
  62.                 }
  63.                 _AllowedProtocols = allowedProtocols;
  64.                 _HashCode ^= (int)_AllowedProtocols;
  65.             }
  66.             //
  67.             public override int GetHashCode()
  68.             {
  69.                 return _HashCode;
  70.             }
  71.             //
  72.             public static bool operator ==(SslCredKey sslCredKey1, SslCredKey sslCredKey2)
  73.             {
  74.                 if ((object)sslCredKey1 == (object)sslCredKey2) {
  75.                     return true;
  76.                 }
  77.                 if ((object)sslCredKey1 == null || (object)sslCredKey2 == null) {
  78.                     return false;
  79.                 }
  80.                 return sslCredKey1.Equals(sslCredKey2);
  81.             }
  82.             //
  83.             public static bool operator !=(SslCredKey sslCredKey1, SslCredKey sslCredKey2)
  84.             {
  85.                 if ((object)sslCredKey1 == (object)sslCredKey2) {
  86.                     return false;
  87.                 }
  88.                 if ((object)sslCredKey1 == null || (object)sslCredKey2 == null) {
  89.                     return true;
  90.                 }
  91.                 return !sslCredKey1.Equals(sslCredKey2);
  92.             }
  93.             //
  94.             public override bool Equals(object y)
  95.             {
  96.                 SslCredKey she = (SslCredKey)y;
  97.                
  98.                 if (_CertThumbPrint.Length != she._CertThumbPrint.Length)
  99.                     return false;
  100.                
  101.                 if (_HashCode != she._HashCode)
  102.                     return false;
  103.                
  104.                 for (int i = 0; i < _CertThumbPrint.Length; ++i)
  105.                     if (_CertThumbPrint[i] != she._CertThumbPrint[i])
  106.                         return false;
  107.                
  108.                 return true;
  109.             }
  110.         }
  111.        
  112.        
  113.         //
  114.         // Returns null or previously cached cred handle
  115.         //
  116.         // ATTN: The returned handle can be invalid, the callers of InitializeSecurityContext and AcceptSecurityContext
  117.         // must be prepared to execute a backout code if the call fails.
  118.         //
  119.         // Note:thumbPrint is a cryptographicaly strong hash of a certificate
  120.         //
  121.         static internal SafeFreeCredentials TryCachedCredential(byte[] thumbPrint, SchProtocols allowedProtocols)
  122.         {
  123.             if (s_CachedCreds.Count == 0) {
  124.                 GlobalLog.Print("TryCachedCredential() Not Found, Current Cache Count = " + s_CachedCreds.Count);
  125.                 return null;
  126.             }
  127.            
  128.             object key = new SslCredKey(thumbPrint, allowedProtocols);
  129.            
  130.             SafeCredentialReference cached = s_CachedCreds[key] as SafeCredentialReference;
  131.            
  132.             if (cached == null || cached.IsClosed || cached._Target.IsInvalid) {
  133.                 GlobalLog.Print("TryCachedCredential() Not Found, Current Cache Count = " + s_CachedCreds.Count);
  134.                 return null;
  135.             }
  136.            
  137.             GlobalLog.Print("TryCachedCredential() Found a cached Handle = " + cached._Target.ToString());
  138.            
  139.             return cached._Target;
  140.         }
  141.         //
  142.         // The app is calling this method after starting an SSL handshake.
  143.         //
  144.         // ATTN: The thumbPrint must be from inspected and possbly cloned user Cert object or we get a security hole in SslCredKey ctor.
  145.         //
  146.         static internal void CacheCredential(SafeFreeCredentials creds, byte[] thumbPrint, SchProtocols allowedProtocols)
  147.         {
  148.             GlobalLog.Assert(creds != null, "CacheCredential|creds == null");
  149.             if (creds.IsInvalid) {
  150.                 GlobalLog.Print("CacheCredential() Refused to cache an Invalid Handle = " + creds.ToString() + ", Current Cache Count = " + s_CachedCreds.Count);
  151.                 return;
  152.             }
  153.            
  154.             object key = new SslCredKey(thumbPrint, allowedProtocols);
  155.            
  156.             SafeCredentialReference cached = s_CachedCreds[key] as SafeCredentialReference;
  157.            
  158.             if (cached == null || cached.IsClosed || cached._Target.IsInvalid) {
  159.                 lock (s_CachedCreds) {
  160.                     cached = s_CachedCreds[key] as SafeCredentialReference;
  161.                    
  162.                     if (cached == null || cached.IsClosed) {
  163.                         cached = SafeCredentialReference.CreateReference(creds);
  164.                        
  165.                         if (cached == null) {
  166.                             // Means the handle got closed in between, return it back and let caller deal with the issue.
  167.                             return;
  168.                         }
  169.                        
  170.                         s_CachedCreds[key] = cached;
  171.                         GlobalLog.Print("CacheCredential() Caching New Handle = " + creds.ToString() + ", Current Cache Count = " + s_CachedCreds.Count);
  172.                        
  173.                         //
  174.                         // A simplest way of preventing infinite cache grows.
  175.                         //
  176.                         // Security relief (DoS):
  177.                         // A number of active creds is never greater than a number of _outstanding_
  178.                         // security sessions, i.e. ssl connections.
  179.                         // So we will try to shrink cache to the number of active creds once in a while.
  180.                         //
  181.                         // Just to make clear we won't shrink cache in the case when NO new handles are coming to it.
  182.                         //
  183.                         if ((s_CachedCreds.Count % c_CheckExpiredModulo) == 0) {
  184.                             DictionaryEntry[] toRemoveAttempt = new DictionaryEntry[s_CachedCreds.Count];
  185.                             s_CachedCreds.CopyTo(toRemoveAttempt, 0);
  186.                            
  187.                             for (int i = 0; i < toRemoveAttempt.Length; ++i) {
  188.                                 cached = toRemoveAttempt[i].Value as SafeCredentialReference;
  189.                                
  190.                                 if (cached != null) {
  191.                                     creds = cached._Target;
  192.                                     cached.Close();
  193.                                    
  194.                                     if (!creds.IsClosed && !creds.IsInvalid && (cached = SafeCredentialReference.CreateReference(creds)) != null)
  195.                                         s_CachedCreds[toRemoveAttempt[i].Key] = cached;
  196.                                     else
  197.                                         s_CachedCreds.Remove(toRemoveAttempt[i].Key);
  198.                                 }
  199.                             }
  200.                             GlobalLog.Print("Scavenged cache, New Cache Count = " + s_CachedCreds.Count);
  201.                         }
  202.                     }
  203.                     else {
  204.                         GlobalLog.Print("CacheCredential() (locked retry) Found already cached Handle = " + cached._Target.ToString());
  205.                     }
  206.                 }
  207.             }
  208.             else {
  209.                 GlobalLog.Print("CacheCredential() Ignoring incoming handle = " + creds.ToString() + " since found already cached Handle = " + cached._Target.ToString());
  210.             }
  211.         }
  212.     }
  213. }

Developer Fusion