Incorrect warning message on thread pool startup in full AOT:ed Windows build.
[mono.git] / mcs / class / referencesource / System / net / System / Net / AuthenticationManager.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="AuthenticationManager.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8
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;
18     using System;
19     using System.Threading;
20     using System.Diagnostics;
21     using System.Diagnostics.CodeAnalysis;
22
23     //
24     // A contract that applications can use to restrict auth scenarios in current appDomain
25     //
26     public interface ICredentialPolicy
27     {
28         bool ShouldSendCredential(
29             Uri challengeUri, 
30             WebRequest request, 
31             NetworkCredential credential, 
32             IAuthenticationModule authenticationModule);
33     }
34
35     /// <devdoc>
36     ///    <para>Manages the authentication modules called during the client authentication
37     ///       process.</para>
38     /// </devdoc>
39     public class AuthenticationManager 
40     {
41         private static object instanceLock = new object();
42         private static IAuthenticationManager internalInstance = null;
43         internal const string authenticationManagerRoot = "System.Net.AuthenticationManager";
44         
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";
48         
49         private AuthenticationManager()
50         {
51         }
52
53         private static IAuthenticationManager Instance 
54         {
55             get
56             {
57                 if (internalInstance == null)
58                 {
59                     lock (instanceLock)
60                     {
61                         if (internalInstance == null)
62                         {
63                             internalInstance = SelectAuthenticationManagerInstance();
64                         }
65                     }
66                 }
67
68                 return internalInstance;
69             }
70         }
71
72         private static IAuthenticationManager SelectAuthenticationManagerInstance()
73         {
74             bool highPerformance = false;
75
76             try
77             {
78                 if (RegistryConfiguration.GlobalConfigReadInt(configHighPerformance, 0) == 1)
79                 {
80                     highPerformance = true;
81                 }
82                 else if (RegistryConfiguration.AppConfigReadInt(configHighPerformance, 0) == 1)
83                 {
84                     highPerformance = true;
85                 }
86             
87                 if (highPerformance)
88                 {
89                     int? maxPrefixLookupEntries = ReadPrefixLookupMaxEntriesConfig();
90                     if ((maxPrefixLookupEntries != null) && (maxPrefixLookupEntries > 0))
91                     {
92                         return new AuthenticationManager2((int)maxPrefixLookupEntries);
93                     }
94                     else
95                     {
96                         return new AuthenticationManager2();
97                     }
98                 }
99             }
100             catch (Exception e)
101             {
102                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
103                 {
104                     throw;
105                 }
106             }
107             
108             return new AuthenticationManagerDefault();
109         }
110
111         private static int? ReadPrefixLookupMaxEntriesConfig()
112         {
113             int? maxPrefixLookupEntries = null;
114
115             int configuredMaxPrefixLookupEntries =
116                 RegistryConfiguration.GlobalConfigReadInt(configPrefixLookupMaxCount, -1);
117
118             if (configuredMaxPrefixLookupEntries > 0)
119             {
120                 maxPrefixLookupEntries = configuredMaxPrefixLookupEntries;
121             }
122
123             // Per-process setting will override global configuration.
124             configuredMaxPrefixLookupEntries =
125                 RegistryConfiguration.AppConfigReadInt(configPrefixLookupMaxCount, -1);
126
127             if (configuredMaxPrefixLookupEntries > 0)
128             {
129                 maxPrefixLookupEntries = configuredMaxPrefixLookupEntries;
130             }
131             return maxPrefixLookupEntries;
132         }
133                 
134         public static ICredentialPolicy CredentialPolicy {
135             get 
136             {
137                 return Instance.CredentialPolicy; 
138             }
139
140             set 
141             {
142                 ExceptionHelper.ControlPolicyPermission.Demand();
143                 Instance.CredentialPolicy = value;
144             }
145         }
146
147         public static StringDictionary CustomTargetNameDictionary 
148         {
149             get 
150             {
151                 return Instance.CustomTargetNameDictionary;  
152             }
153         }
154
155         internal static SpnDictionary SpnDictionary 
156         {
157             get 
158             {
159                 return Instance.SpnDictionary;
160             }
161         }
162
163         internal static void EnsureConfigLoaded() 
164         {
165             Instance.EnsureConfigLoaded();          
166         }
167
168         internal static bool OSSupportsExtendedProtection 
169         {
170             get
171             {
172                 return Instance.OSSupportsExtendedProtection;
173             }
174         }
175
176         internal static bool SspSupportsExtendedProtection 
177         {
178             get 
179             {
180                 return Instance.SspSupportsExtendedProtection;
181             }
182         }
183
184         /// <devdoc>
185         ///    <para>Call each registered authentication module to determine the first module that
186         ///       can respond to the authentication request.</para>
187         /// </devdoc>
188         public static Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials) 
189         {
190             return Instance.Authenticate(challenge, request, credentials);
191         }
192
193         /// <devdoc>
194         ///    <para>Pre-authenticates a request.</para>
195         /// </devdoc>
196         public static Authorization PreAuthenticate(WebRequest request, ICredentials credentials) 
197         {
198             return Instance.PreAuthenticate(request, credentials);
199         }
200
201         /// <devdoc>
202         ///    <para>Registers an authentication module with the authentication manager.</para>
203         /// </devdoc>
204         public static void Register(IAuthenticationModule authenticationModule) 
205         {
206             ExceptionHelper.UnmanagedPermission.Demand();
207             Instance.Register(authenticationModule);
208         }
209
210         /// <devdoc>
211         ///    <para>Unregisters authentication modules for an authentication scheme.</para>
212         /// </devdoc>
213         public static void Unregister(IAuthenticationModule authenticationModule) 
214         {
215             ExceptionHelper.UnmanagedPermission.Demand();
216             Instance.Unregister(authenticationModule);
217         }
218
219         /// <devdoc>
220         ///    <para>Unregisters authentication modules for an authentication scheme.</para>
221         /// </devdoc>
222         public static void Unregister(string authenticationScheme) 
223         {
224             ExceptionHelper.UnmanagedPermission.Demand();
225             Instance.Unregister(authenticationScheme);
226         }
227
228         /// <devdoc>
229         ///    <para>
230         ///       Returns a list of registered authentication modules.
231         ///    </para>
232         /// </devdoc>
233         public static IEnumerator RegisteredModules 
234         {
235             get 
236             {
237                 return Instance.RegisteredModules;
238             }
239         }
240
241         /// <devdoc>
242         ///    <para>
243         ///       Binds an authentication response to a request for pre-authentication.
244         ///    </para>
245         /// </devdoc>
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) 
251         {
252             Instance.BindModule(uri, response, module);
253         }
254
255         //
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 ...
259         //
260         [SuppressMessage(
261             "Microsoft.Globalization", "CA1308", Justification = "Assert-only by check for lower-case signature")]
262         internal static int FindSubstringNotInQuotes(string challenge, string signature) 
263         {
264             int index = -1;
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)
268             {
269                 int firstQuote = -1, secondQuote = -1;
270                 for (int i = 0; i < challenge.Length && index < 0; i++)
271                 {
272                     // Search for the quotes
273                     if (challenge[i] == '\"')
274                     {
275                         if (firstQuote <= secondQuote)
276                             firstQuote = i;
277                         else
278                             secondQuote = i;
279                     }
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))
282                     {
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)
289                             continue;
290
291                         int checkstart = secondQuote + 1;
292                         int checkLength = firstQuote - secondQuote - 1;
293                         do
294                         {
295                             // Search for the next (partial match) occurance of the signature
296                             index = IndexOf(challenge, signature, checkstart, checkLength);
297
298                             if (index >= 0)
299                             {
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] == ','))
303                                 {
304                                     break;
305                                 }
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;
309                             }
310                         } while (index >= 0);
311                     }
312                 }
313             }
314             GlobalLog.Print("AuthenticationManager::FindSubstringNotInQuotes(" + challenge + ", " + signature + ")=" + index.ToString());
315             return index;
316         }
317
318         //
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)
322         {
323             count += start + 1 - lwrCaseSignature.Length;
324             for (; start < count; ++start)
325             {
326                 int i = 0;
327                 for (; i < lwrCaseSignature.Length; ++i)
328                 {
329                     // force a challenge char to lowecase (safe assuming it works on trusted ASCII source)
330                     if ((challenge[start + i] | 0x20) != lwrCaseSignature[i])
331                         break;
332                 }
333                 if (i == lwrCaseSignature.Length)
334                     return start;
335             }
336             return -1;
337         }
338
339         //
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.
345         //
346         internal static int SplitNoQuotes(string challenge, ref int offset) 
347         {
348             // GlobalLog.Print("SplitNoQuotes([" + challenge + "], " + offset.ToString() + ")");
349             //
350             // save offset
351             //
352             int realOffset = offset;
353             //
354             // default is not found
355             //
356             offset = -1;
357
358             if (challenge != null && realOffset < challenge.Length)
359             {
360                 int firstQuote = -1, secondQuote = -1;
361
362                 for (int i = realOffset; i < challenge.Length; i++)
363                 {
364                     //
365                     // firstQuote>secondQuote means we are in a quoted string
366                     //
367                     if (firstQuote > secondQuote && challenge[i] == '\\' && i + 1 < challenge.Length && challenge[i + 1] == '\"')
368                     {
369                         //
370                         // skip <\"> when in a quoted string
371                         //
372                         i++;
373                     }
374                     else if (challenge[i] == '\"')
375                     {
376                         if (firstQuote <= secondQuote)
377                         {
378                             firstQuote = i;
379                         }
380                         else
381                         {
382                             secondQuote = i;
383                         }
384                     }
385                     else if (challenge[i] == '=' && firstQuote <= secondQuote && offset < 0)
386                     {
387                         offset = i;
388                     }
389                     else if (challenge[i] == ',' && firstQuote <= secondQuote)
390                     {
391                         return i;
392                     }
393                 }
394             }
395
396             return -1;
397         }
398
399 #if !FEATURE_PAL
400         internal static Authorization GetGroupAuthorization(
401             IAuthenticationModule thisModule, 
402             string token, 
403             bool finished, 
404             NTAuthentication authSession, 
405             bool shareAuthenticatedConnections, 
406             bool mutualAuth) 
407         {
408             return new Authorization(
409                     token,
410                     finished,
411                     (shareAuthenticatedConnections) ? null 
412                         : (thisModule.GetType().FullName + "/" + authSession.UniqueUserId),
413                     mutualAuth);
414         }
415 #endif // !FEATURE_PAL
416
417     } // class AuthenticationManager
418
419 } // namespace System.Net