We need you! We're working hard on the next version of Developer Fusion - Let us know what you think we should be up to!

The Labs \ Source Viewer \ SSCLI \ System.Net \ AuthenticationManager

  1. //------------------------------------------------------------------------------
  2. // <copyright file="AuthenticationManager.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. namespace System.Net
  16. {
  17.    
  18.     using System.Collections;
  19.     using System.Collections.Generic;
  20.     using System.Collections.Specialized;
  21.     using System.Configuration;
  22.     using System.Globalization;
  23.     using System.Net.Configuration;
  24.     using System.Reflection;
  25.     using System.Security.Permissions;
  26.     using System;
  27.     using System.Threading;
  28.    
  29.    
  30.     //
  31.     // A contract that applications can use to restrict auth scenarios in current appDomain
  32.     //
  33.     public interface ICredentialPolicy
  34.     {
  35.         bool ShouldSendCredential(Uri challengeUri, WebRequest request, NetworkCredential credential, IAuthenticationModule authenticationModule);
  36.     }
  37.    
  38.     /// <devdoc>
  39.     /// <para>Manages the authentication modules called during the client authentication
  40.     /// process.</para>
  41.     /// </devdoc>
  42.     public class AuthenticationManager
  43.     {
  44.        
  45.         //also used as a lock object
  46.         private static PrefixLookup s_ModuleBinding = new PrefixLookup();
  47.        
  48.         private static ArrayList s_ModuleList;
  49.         private static ICredentialPolicy s_ICredentialPolicy;
  50.         private static SpnDictionary m_SpnDictionary = new SpnDictionary();
  51.        
  52.         // not creatable...
  53.         //
  54.         private AuthenticationManager()
  55.         {
  56.         }
  57.        
  58.         //
  59.         //
  60.         //
  61.         public static ICredentialPolicy CredentialPolicy {
  62.             get { return s_ICredentialPolicy; }
  63.             set {
  64.                 ExceptionHelper.ControlPolicyPermission.Demand();
  65.                 s_ICredentialPolicy = value;
  66.             }
  67.         }
  68.         //
  69.         //
  70.         public static StringDictionary CustomTargetNameDictionary {
  71.             get { return m_SpnDictionary; }
  72.         }
  73.         //
  74.         // This will give access to some internal methods
  75.         //
  76.         static internal SpnDictionary SpnDictionary {
  77.             get { return m_SpnDictionary; }
  78.         }
  79.        
  80.         //
  81.         //
  82.         static internal void EnsureConfigLoaded()
  83.         {
  84.             try {
  85.                 object o = ModuleList;
  86.             }
  87.             catch (Exception e) {
  88.                 if (e is ThreadAbortException || e is OutOfMemoryException || e is StackOverflowException)
  89.                     throw;
  90.                 // A Config System has circular dependency on HttpWebRequest so they call this method to
  91.                 // trigger the config. For some reason they don't want any exceptions from here.
  92.             }
  93.             catch {
  94.                 // A Config System has circular dependency on HttpWebRequest so they call this method to
  95.                 // trigger the config. For some reason they don't want any exceptions from here.
  96.             }
  97.         }
  98.        
  99.         //
  100.         // ModuleList - static initialized property -
  101.         // contains list of Modules used for Authentication
  102.         //
  103.        
  104.         private static ArrayList ModuleList {
  105.            
  106.             get {
  107.                
  108.                 //
  109.                 // GetConfig() might use us, so we have a circular dependency issue,
  110.                 // that causes us to nest here, we grab the lock, only
  111.                 // if we haven't initialized, or another thread is busy in initialization
  112.                 //
  113.                
  114.                 if (s_ModuleList == null) {
  115.                     lock (s_ModuleBinding) {
  116.                         if (s_ModuleList == null) {
  117.                             GlobalLog.Print("AuthenticationManager::Initialize(): calling ConfigurationManager.GetSection()");
  118.                            
  119.                             // This will never come back as null. Additionally, it will
  120.                             // have the items the user wants available.
  121.                             List<Type> authenticationModuleTypes = AuthenticationModulesSectionInternal.GetSection().AuthenticationModules;
  122.                            
  123.                             //
  124.                             // Should be registered in a growing list of encryption/algorithm strengths
  125.                             // basically, walk through a list of Types, and create new Auth objects
  126.                             // from them.
  127.                             //
  128.                             // order is meaningful here:
  129.                             // load the registered list of auth types
  130.                             // with growing level of encryption.
  131.                             //
  132.                            
  133.                             ArrayList moduleList = new ArrayList();
  134.                             IAuthenticationModule moduleToRegister;
  135.                             foreach (Type type in authenticationModuleTypes) {
  136.                                 try {
  137.                                         // Binder
  138.                                         // no arguments
  139.                                     moduleToRegister = Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, new object[0], CultureInfo.InvariantCulture) as IAuthenticationModule;
  140.                                     if (moduleToRegister != null) {
  141.                                         GlobalLog.Print("WebRequest::Initialize(): Register:" + moduleToRegister.AuthenticationType);
  142.                                         RemoveAuthenticationType(moduleList, moduleToRegister.AuthenticationType);
  143.                                         moduleList.Add(moduleToRegister);
  144.                                     }
  145.                                 }
  146.                                 catch (Exception exception) {
  147.                                     //
  148.                                     // ignore failure (log exception for debugging)
  149.                                     //
  150.                                     GlobalLog.Print("AuthenticationManager::constructor failed to initialize: " + exception.ToString());
  151.                                 }
  152.                                 catch {
  153.                                     //
  154.                                     // ignore failure (log exception for debugging)
  155.                                     //
  156.                                     GlobalLog.Print("AuthenticationManager::constructor failed to initialize: Non-CLS Compliant Exception");
  157.                                 }
  158.                             }
  159.                            
  160.                             s_ModuleList = moduleList;
  161.                         }
  162.                     }
  163.                 }
  164.                
  165.                 return s_ModuleList;
  166.             }
  167.         }
  168.        
  169.        
  170.         private static void RemoveAuthenticationType(ArrayList list, string typeToRemove)
  171.         {
  172.             for (int i = 0; i < list.Count; ++i) {
  173.                 if (string.Compare(((IAuthenticationModule)list[i]).AuthenticationType, typeToRemove, StringComparison.OrdinalIgnoreCase) == 0) {
  174.                     list.RemoveAt(i);
  175.                     break;
  176.                 }
  177.                
  178.             }
  179.         }
  180.        
  181.         /// <devdoc>
  182.         /// <para>Call each registered authentication module to determine the first module that
  183.         /// can respond to the authentication request.</para>
  184.         /// </devdoc>
  185.         public static Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials)
  186.         {
  187.             //
  188.             // parameter validation
  189.             //
  190.             if (request == null) {
  191.                 throw new ArgumentNullException("request");
  192.             }
  193.             if (credentials == null) {
  194.                 throw new ArgumentNullException("credentials");
  195.             }
  196.             if (challenge == null) {
  197.                 throw new ArgumentNullException("challenge");
  198.             }
  199.            
  200.             GlobalLog.Print("AuthenticationManager::Authenticate() challenge:[" + challenge + "]");
  201.            
  202.             Authorization response = null;
  203.            
  204.             HttpWebRequest httpWebRequest = request as HttpWebRequest;
  205.             if (httpWebRequest != null && httpWebRequest.CurrentAuthenticationState.Module != null) {
  206.                 response = httpWebRequest.CurrentAuthenticationState.Module.Authenticate(challenge, request, credentials);
  207.             }
  208.             else {
  209.                 // This is the case where we would try to find the module on the first server challenge
  210.                 lock (s_ModuleBinding) {
  211.                     //
  212.                     // fastest way of iterating on the ArryList
  213.                     //
  214.                     for (int i = 0; i < ModuleList.Count; i++) {
  215.                         IAuthenticationModule authenticationModule = (IAuthenticationModule)ModuleList[i];
  216.                         //
  217.                         // the AuthenticationModule will
  218.                         // 1) return a valid string on success
  219.                         // 2) return null if it knows it cannot respond
  220.                         // 3) throw if it could have responded but unexpectedly failed to do so
  221.                         //
  222.                         if (httpWebRequest != null) {
  223.                             httpWebRequest.CurrentAuthenticationState.Module = authenticationModule;
  224.                         }
  225.                         response = authenticationModule.Authenticate(challenge, request, credentials);
  226.                        
  227.                         if (response != null) {
  228.                             //
  229.                             // found the Authentication Module, return it
  230.                             //
  231.                             GlobalLog.Print("AuthenticationManager::Authenticate() found IAuthenticationModule:[" + authenticationModule.AuthenticationType + "]");
  232.                             break;
  233.                         }
  234.                     }
  235.                 }
  236.             }
  237.            
  238.             return response;
  239.         }
  240.        
  241.         /// <devdoc>
  242.         /// <para>Pre-authenticates a request.</para>
  243.         /// </devdoc>
  244.         public static Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
  245.         {
  246.             GlobalLog.Print("AuthenticationManager::PreAuthenticate() request:" + ValidationHelper.HashString(request) + " credentials:" + ValidationHelper.HashString(credentials));
  247.             if (request == null) {
  248.                 throw new ArgumentNullException("request");
  249.             }
  250.             if (credentials == null) {
  251.                 return null;
  252.             }
  253.            
  254.             HttpWebRequest httpWebRequest = request as HttpWebRequest;
  255.             IAuthenticationModule authenticationModule;
  256.             if (httpWebRequest == null)
  257.                 return null;
  258.            
  259.             //
  260.             // PrefixLookup is thread-safe
  261.             //
  262.             string moduleName = s_ModuleBinding.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri) as string;
  263.             GlobalLog.Print("AuthenticationManager::PreAuthenticate() s_ModuleBinding.Lookup returns:" + ValidationHelper.ToString(moduleName));
  264.             if (moduleName == null)
  265.                 return null;
  266.             authenticationModule = findModule(moduleName);
  267.             if (authenticationModule == null) {
  268.                 // The module could have been unregistered
  269.                 // No preauthentication is possible
  270.                 return null;
  271.             }
  272.            
  273.             // Otherwise invoke the PreAuthenticate method
  274.             // we're guaranteed that CanPreAuthenticate is true because we check before calling BindModule()
  275.             Authorization authorization = authenticationModule.PreAuthenticate(request, credentials);
  276.            
  277.             if (authorization != null && !authorization.Complete && httpWebRequest != null)
  278.                 httpWebRequest.CurrentAuthenticationState.Module = authenticationModule;
  279.            
  280.             GlobalLog.Print("AuthenticationManager::PreAuthenticate() IAuthenticationModule.PreAuthenticate() returned authorization:" + ValidationHelper.HashString(authorization));
  281.             return authorization;
  282.         }
  283.        
  284.        
  285.         /// <devdoc>
  286.         /// <para>Registers an authentication module with the authentication manager.</para>
  287.         /// </devdoc>
  288.         public static void Register(IAuthenticationModule authenticationModule)
  289.         {
  290.             ExceptionHelper.UnmanagedPermission.Demand();
  291.             if (authenticationModule == null) {
  292.                 throw new ArgumentNullException("authenticationModule");
  293.             }
  294.             GlobalLog.Print("AuthenticationManager::Register() registering :[" + authenticationModule.AuthenticationType + "]");
  295.             lock (s_ModuleBinding) {
  296.                 IAuthenticationModule existentModule = findModule(authenticationModule.AuthenticationType);
  297.                 if (existentModule != null) {
  298.                     ModuleList.Remove(existentModule);
  299.                 }
  300.                 ModuleList.Add(authenticationModule);
  301.             }
  302.         }
  303.        
  304.         /// <devdoc>
  305.         /// <para>Unregisters authentication modules for an authentication scheme.</para>
  306.         /// </devdoc>
  307.         public static void Unregister(IAuthenticationModule authenticationModule)
  308.         {
  309.             ExceptionHelper.UnmanagedPermission.Demand();
  310.             if (authenticationModule == null) {
  311.                 throw new ArgumentNullException("authenticationModule");
  312.             }
  313.             GlobalLog.Print("AuthenticationManager::Unregister() unregistering :[" + authenticationModule.AuthenticationType + "]");
  314.             lock (s_ModuleBinding) {
  315.                 if (!ModuleList.Contains(authenticationModule)) {
  316.                     throw new InvalidOperationException(SR.GetString(SR.net_authmodulenotregistered));
  317.                 }
  318.                 ModuleList.Remove(authenticationModule);
  319.             }
  320.         }
  321.         /// <devdoc>
  322.         /// <para>Unregisters authentication modules for an authentication scheme.</para>
  323.         /// </devdoc>
  324.         public static void Unregister(string authenticationScheme)
  325.         {
  326.             ExceptionHelper.UnmanagedPermission.Demand();
  327.             if (authenticationScheme == null) {
  328.                 throw new ArgumentNullException("authenticationScheme");
  329.             }
  330.             GlobalLog.Print("AuthenticationManager::Unregister() unregistering :[" + authenticationScheme + "]");
  331.             lock (s_ModuleBinding) {
  332.                 IAuthenticationModule existentModule = findModule(authenticationScheme);
  333.                 if (existentModule == null) {
  334.                     throw new InvalidOperationException(SR.GetString(SR.net_authschemenotregistered));
  335.                 }
  336.                 ModuleList.Remove(existentModule);
  337.             }
  338.         }
  339.        
  340.         /// <devdoc>
  341.         /// <para>
  342.         /// Returns a list of registered authentication modules.
  343.         /// </para>
  344.         /// </devdoc>
  345.         public static IEnumerator RegisteredModules {
  346.             get { return ModuleList.GetEnumerator(); }
  347.         }
  348.        
  349.         /// <devdoc>
  350.         /// <para>
  351.         /// Binds an authentication response to a request for pre-authentication.
  352.         /// </para>
  353.         /// </devdoc>
  354.         // Create binding between an authorization response and the module
  355.         // generating that response
  356.         // This association is used for deciding which module to invoke
  357.         // for preauthentication purposes
  358.         static internal void BindModule(Uri uri, Authorization response, IAuthenticationModule module)
  359.         {
  360.             GlobalLog.Assert(module.CanPreAuthenticate, "AuthenticationManager::BindModule()|module.CanPreAuthenticate == false");
  361.             if (response.ProtectionRealm != null) {
  362.                 // The authentication module specified which Uri prefixes
  363.                 // will be preauthenticated
  364.                 string[] prefix = response.ProtectionRealm;
  365.                
  366.                 for (int k = 0; k < prefix.Length; k++) {
  367.                     //
  368.                     // PrefixLookup is thread-safe
  369.                     //
  370.                     s_ModuleBinding.Add(prefix[k], module.AuthenticationType);
  371.                 }
  372.             }
  373.             else {
  374.                 // Otherwise use the default policy for "fabricating"
  375.                 // some protection realm generalizing the particular Uri
  376.                 string prefix = generalize(uri);
  377.                 //
  378.                 // PrefixLookup is thread-safe
  379.                 //
  380.                 s_ModuleBinding.Add(prefix, module.AuthenticationType);
  381.             }
  382.         }
  383.        
  384.         //
  385.         // Lookup module by AuthenticationType
  386.         //
  387.         private static IAuthenticationModule findModule(string authenticationType)
  388.         {
  389.             IAuthenticationModule returnAuthenticationModule = null;
  390.             ArrayList moduleList = ModuleList;
  391.             IAuthenticationModule authenticationModule;
  392.             for (int k = 0; k < moduleList.Count; k++) {
  393.                 authenticationModule = (IAuthenticationModule)moduleList[k];
  394.                 if (string.Compare(authenticationModule.AuthenticationType, authenticationType, StringComparison.OrdinalIgnoreCase) == 0) {
  395.                     returnAuthenticationModule = authenticationModule;
  396.                     break;
  397.                 }
  398.             }
  399.             return returnAuthenticationModule;
  400.         }
  401.        
  402.         // This function returns a prefix of the given absolute Uri
  403.         // which will be used for associating authentication information
  404.         // The purpose is to associate the module-binding not with a single
  405.         // Uri but some collection generalizing that Uri to the loosely-defined
  406.         // notion of "protection realm"
  407.         private static string generalize(Uri location)
  408.         {
  409.             string completeUri = location.AbsoluteUri;
  410.             int lastFwdSlash = completeUri.LastIndexOf('/');
  411.             if (lastFwdSlash < 0) {
  412.                 return completeUri;
  413.             }
  414.             return completeUri.Substring(0, lastFwdSlash + 1);
  415.         }
  416.        
  417.         //
  418.         // The method will extract the blob that does correspond to the moduled with the name passed in signature parameter
  419.         // The method avoids confusion arisen from the parameters passed in a quoted string, such as:
  420.         // WWW-Authenticate: Digest username="NTLM", realm="wit", NTLM ...
  421.         //
  422.         static internal int FindSubstringNotInQuotes(string challenge, string signature)
  423.         {
  424.             int index = -1;
  425.             if (challenge != null && signature != null && challenge.Length >= signature.Length) {
  426.                 int firstQuote = -1;
  427.                 int secondQuote = -1;
  428.                 for (int i = 0; i < challenge.Length; i++) {
  429.                     if (challenge[i] == '"') {
  430.                         if (firstQuote <= secondQuote)
  431.                             firstQuote = i;
  432.                         else
  433.                             secondQuote = i;
  434.                     }
  435.                    
  436.                     if (i == challenge.Length - 1 || (challenge[i] == '"' && firstQuote > secondQuote)) {
  437.                         // see if the portion of challenge out of the quotes contains
  438.                         // the signature of the IAuthenticationModule
  439.                         if (i == challenge.Length - 1)
  440.                             firstQuote = challenge.Length;
  441.                        
  442.