1 //------------------------------------------------------------------------------
2 // <copyright file="AuthenticationManager.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Collections.Specialized;
12 using System.Configuration;
13 using System.Globalization;
14 using System.Net.Configuration;
15 using System.Reflection;
16 using System.Security.Authentication.ExtendedProtection;
17 using System.Security.Permissions;
19 using System.Threading;
20 using System.Diagnostics;
21 using System.Diagnostics.CodeAnalysis;
24 // A contract that applications can use to restrict auth scenarios in current appDomain
26 public interface ICredentialPolicy
28 bool ShouldSendCredential(
31 NetworkCredential credential,
32 IAuthenticationModule authenticationModule);
36 /// <para>Manages the authentication modules called during the client authentication
39 public class AuthenticationManager
41 private static object instanceLock = new object();
42 private static IAuthenticationManager internalInstance = null;
43 internal const string authenticationManagerRoot = "System.Net.AuthenticationManager";
45 // Following names are used both as a per-app key as a global setting
46 internal const string configHighPerformance = authenticationManagerRoot + ".HighPerformance";
47 internal const string configPrefixLookupMaxCount = authenticationManagerRoot + ".PrefixLookupMaxCount";
49 private AuthenticationManager()
53 private static IAuthenticationManager Instance
57 if (internalInstance == null)
61 if (internalInstance == null)
63 internalInstance = SelectAuthenticationManagerInstance();
68 return internalInstance;
72 private static IAuthenticationManager SelectAuthenticationManagerInstance()
74 bool highPerformance = false;
78 if (RegistryConfiguration.GlobalConfigReadInt(configHighPerformance, 0) == 1)
80 highPerformance = true;
82 else if (RegistryConfiguration.AppConfigReadInt(configHighPerformance, 0) == 1)
84 highPerformance = true;
89 int? maxPrefixLookupEntries = ReadPrefixLookupMaxEntriesConfig();
90 if ((maxPrefixLookupEntries != null) && (maxPrefixLookupEntries > 0))
92 return new AuthenticationManager2((int)maxPrefixLookupEntries);
96 return new AuthenticationManager2();
102 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
108 return new AuthenticationManagerDefault();
111 private static int? ReadPrefixLookupMaxEntriesConfig()
113 int? maxPrefixLookupEntries = null;
115 int configuredMaxPrefixLookupEntries =
116 RegistryConfiguration.GlobalConfigReadInt(configPrefixLookupMaxCount, -1);
118 if (configuredMaxPrefixLookupEntries > 0)
120 maxPrefixLookupEntries = configuredMaxPrefixLookupEntries;
123 // Per-process setting will override global configuration.
124 configuredMaxPrefixLookupEntries =
125 RegistryConfiguration.AppConfigReadInt(configPrefixLookupMaxCount, -1);
127 if (configuredMaxPrefixLookupEntries > 0)
129 maxPrefixLookupEntries = configuredMaxPrefixLookupEntries;
131 return maxPrefixLookupEntries;
134 public static ICredentialPolicy CredentialPolicy {
137 return Instance.CredentialPolicy;
142 ExceptionHelper.ControlPolicyPermission.Demand();
143 Instance.CredentialPolicy = value;
147 public static StringDictionary CustomTargetNameDictionary
151 return Instance.CustomTargetNameDictionary;
155 internal static SpnDictionary SpnDictionary
159 return Instance.SpnDictionary;
163 internal static void EnsureConfigLoaded()
165 Instance.EnsureConfigLoaded();
168 internal static bool OSSupportsExtendedProtection
172 return Instance.OSSupportsExtendedProtection;
176 internal static bool SspSupportsExtendedProtection
180 return Instance.SspSupportsExtendedProtection;
185 /// <para>Call each registered authentication module to determine the first module that
186 /// can respond to the authentication request.</para>
188 public static Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials)
190 return Instance.Authenticate(challenge, request, credentials);
194 /// <para>Pre-authenticates a request.</para>
196 public static Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
198 return Instance.PreAuthenticate(request, credentials);
202 /// <para>Registers an authentication module with the authentication manager.</para>
204 public static void Register(IAuthenticationModule authenticationModule)
206 ExceptionHelper.UnmanagedPermission.Demand();
207 Instance.Register(authenticationModule);
211 /// <para>Unregisters authentication modules for an authentication scheme.</para>
213 public static void Unregister(IAuthenticationModule authenticationModule)
215 ExceptionHelper.UnmanagedPermission.Demand();
216 Instance.Unregister(authenticationModule);
220 /// <para>Unregisters authentication modules for an authentication scheme.</para>
222 public static void Unregister(string authenticationScheme)
224 ExceptionHelper.UnmanagedPermission.Demand();
225 Instance.Unregister(authenticationScheme);
230 /// Returns a list of registered authentication modules.
233 public static IEnumerator RegisteredModules
237 return Instance.RegisteredModules;
243 /// Binds an authentication response to a request for pre-authentication.
246 // Create binding between an authorization response and the module
247 // generating that response
248 // This association is used for deciding which module to invoke
249 // for preauthentication purposes
250 internal static void BindModule(Uri uri, Authorization response, IAuthenticationModule module)
252 Instance.BindModule(uri, response, module);
256 // The method will extract the blob that does correspond to the moduled with the name passed in signature
257 // parameter. The method avoids confusion arisen from the parameters passed in a quoted string, such as:
258 // WWW-Authenticate: Digest username="NTLM", realm="wit", NTLM ...
261 "Microsoft.Globalization", "CA1308", Justification = "Assert-only by check for lower-case signature")]
262 internal static int FindSubstringNotInQuotes(string challenge, string signature)
265 Debug.Assert(signature.ToLowerInvariant().Equals(signature, StringComparison.Ordinal),
266 "'signature' parameter must be lower case");
267 if (challenge != null && signature != null && challenge.Length >= signature.Length)
269 int firstQuote = -1, secondQuote = -1;
270 for (int i = 0; i < challenge.Length && index < 0; i++)
272 // Search for the quotes
273 if (challenge[i] == '\"')
275 if (firstQuote <= secondQuote)
280 // We've found both ends of an unquoted segment (could be whole challenge), search inside for the signature.
281 if (i == challenge.Length - 1 || (challenge[i] == '\"' && firstQuote > secondQuote))
283 // see if the portion of challenge out of the quotes contains
284 // the signature of the IAuthenticationModule
285 if (i == challenge.Length - 1)
286 firstQuote = challenge.Length;
287 // unquoted segment is too small to hold a scheme name, ie: scheme param="value",a=""
288 if (firstQuote < secondQuote + 3)
291 int checkstart = secondQuote + 1;
292 int checkLength = firstQuote - secondQuote - 1;
295 // Search for the next (partial match) occurance of the signature
296 index = IndexOf(challenge, signature, checkstart, checkLength);
300 // Verify the signature is a full scheme name match, not a partial match or a parameter name:
301 if ((index == 0 || challenge[index - 1] == ' ' || challenge[index - 1] == ',') &&
302 (index + signature.Length == challenge.Length || challenge[index + signature.Length] == ' ' || challenge[index + signature.Length] == ','))
306 // Only a partial match / param name, but maybe there is another occurance of the signature later?
307 checkLength -= index - checkstart + 1;
308 checkstart = index + 1;
310 } while (index >= 0);
314 GlobalLog.Print("AuthenticationManager::FindSubstringNotInQuotes(" + challenge + ", " + signature + ")=" + index.ToString());
319 // Helper for FindSubstringNotInQuotes
320 // Find the FIRST possible index of a signature.
321 private static int IndexOf(string challenge, string lwrCaseSignature, int start, int count)
323 count += start + 1 - lwrCaseSignature.Length;
324 for (; start < count; ++start)
327 for (; i < lwrCaseSignature.Length; ++i)
329 // force a challenge char to lowecase (safe assuming it works on trusted ASCII source)
330 if ((challenge[start + i] | 0x20) != lwrCaseSignature[i])
333 if (i == lwrCaseSignature.Length)
340 // this method is called by the IAuthenticationModule implementations
341 // (mainly Digest) to safely find their list of parameters in a challenge.
342 // it returns the index of the first ',' that is not included in quotes,
343 // -1 is returned on error or end of string. on return offset contains the
344 // index of the first '=' that is not included in quotes, -1 if no '=' was found.
346 internal static int SplitNoQuotes(string challenge, ref int offset)
348 // GlobalLog.Print("SplitNoQuotes([" + challenge + "], " + offset.ToString() + ")");
352 int realOffset = offset;
354 // default is not found
358 if (challenge != null && realOffset < challenge.Length)
360 int firstQuote = -1, secondQuote = -1;
362 for (int i = realOffset; i < challenge.Length; i++)
365 // firstQuote>secondQuote means we are in a quoted string
367 if (firstQuote > secondQuote && challenge[i] == '\\' && i + 1 < challenge.Length && challenge[i + 1] == '\"')
370 // skip <\"> when in a quoted string
374 else if (challenge[i] == '\"')
376 if (firstQuote <= secondQuote)
385 else if (challenge[i] == '=' && firstQuote <= secondQuote && offset < 0)
389 else if (challenge[i] == ',' && firstQuote <= secondQuote)
400 internal static Authorization GetGroupAuthorization(
401 IAuthenticationModule thisModule,
404 NTAuthentication authSession,
405 bool shareAuthenticatedConnections,
408 return new Authorization(
411 (shareAuthenticatedConnections) ? null
412 : (thisModule.GetType().FullName + "/" + authSession.UniqueUserId),
415 #endif // !FEATURE_PAL
417 } // class AuthenticationManager
419 } // namespace System.Net