Implement mono_gc_alloc_fixed on Boehm to be uncollectable. This matches SGen behavio...
[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 #if FEATURE_MONO_CAS
143                 ExceptionHelper.ControlPolicyPermission.Demand();
144 #endif
145                 Instance.CredentialPolicy = value;
146             }
147         }
148
149         public static StringDictionary CustomTargetNameDictionary 
150         {
151             get 
152             {
153                 return Instance.CustomTargetNameDictionary;  
154             }
155         }
156
157         internal static SpnDictionary SpnDictionary 
158         {
159             get 
160             {
161                 return Instance.SpnDictionary;
162             }
163         }
164
165         internal static void EnsureConfigLoaded() 
166         {
167             Instance.EnsureConfigLoaded();          
168         }
169
170         internal static bool OSSupportsExtendedProtection 
171         {
172             get
173             {
174                 return Instance.OSSupportsExtendedProtection;
175             }
176         }
177
178         internal static bool SspSupportsExtendedProtection 
179         {
180             get 
181             {
182                 return Instance.SspSupportsExtendedProtection;
183             }
184         }
185
186         /// <devdoc>
187         ///    <para>Call each registered authentication module to determine the first module that
188         ///       can respond to the authentication request.</para>
189         /// </devdoc>
190         public static Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials) 
191         {
192             return Instance.Authenticate(challenge, request, credentials);
193         }
194
195         /// <devdoc>
196         ///    <para>Pre-authenticates a request.</para>
197         /// </devdoc>
198         public static Authorization PreAuthenticate(WebRequest request, ICredentials credentials) 
199         {
200             return Instance.PreAuthenticate(request, credentials);
201         }
202
203         /// <devdoc>
204         ///    <para>Registers an authentication module with the authentication manager.</para>
205         /// </devdoc>
206         public static void Register(IAuthenticationModule authenticationModule) 
207         {
208 #if FEATURE_MONO_CAS
209             ExceptionHelper.UnmanagedPermission.Demand();
210 #endif
211             Instance.Register(authenticationModule);
212         }
213
214         /// <devdoc>
215         ///    <para>Unregisters authentication modules for an authentication scheme.</para>
216         /// </devdoc>
217         public static void Unregister(IAuthenticationModule authenticationModule) 
218         {
219 #if FEATURE_MONO_CAS
220             ExceptionHelper.UnmanagedPermission.Demand();
221 #endif
222             Instance.Unregister(authenticationModule);
223         }
224
225         /// <devdoc>
226         ///    <para>Unregisters authentication modules for an authentication scheme.</para>
227         /// </devdoc>
228         public static void Unregister(string authenticationScheme) 
229         {
230 #if FEATURE_MONO_CAS
231             ExceptionHelper.UnmanagedPermission.Demand();
232 #endif
233             Instance.Unregister(authenticationScheme);
234         }
235
236         /// <devdoc>
237         ///    <para>
238         ///       Returns a list of registered authentication modules.
239         ///    </para>
240         /// </devdoc>
241         public static IEnumerator RegisteredModules 
242         {
243             get 
244             {
245                 return Instance.RegisteredModules;
246             }
247         }
248
249         /// <devdoc>
250         ///    <para>
251         ///       Binds an authentication response to a request for pre-authentication.
252         ///    </para>
253         /// </devdoc>
254         // Create binding between an authorization response and the module
255         // generating that response
256         // This association is used for deciding which module to invoke
257         // for preauthentication purposes
258         internal static void BindModule(Uri uri, Authorization response, IAuthenticationModule module) 
259         {
260             Instance.BindModule(uri, response, module);
261         }
262
263         //
264         // The method will extract the blob that does correspond to the moduled with the name passed in signature 
265         // parameter. The method avoids confusion arisen from the parameters passed in a quoted string, such as:
266         // WWW-Authenticate: Digest username="NTLM", realm="wit", NTLM ...
267         //
268         [SuppressMessage(
269             "Microsoft.Globalization", "CA1308", Justification = "Assert-only by check for lower-case signature")]
270         internal static int FindSubstringNotInQuotes(string challenge, string signature) 
271         {
272             int index = -1;
273             Debug.Assert(signature.ToLowerInvariant().Equals(signature, StringComparison.Ordinal),
274                 "'signature' parameter must be lower case");
275             if (challenge != null && signature != null && challenge.Length >= signature.Length)
276             {
277                 int firstQuote = -1, secondQuote = -1;
278                 for (int i = 0; i < challenge.Length && index < 0; i++)
279                 {
280                     // Search for the quotes
281                     if (challenge[i] == '\"')
282                     {
283                         if (firstQuote <= secondQuote)
284                             firstQuote = i;
285                         else
286                             secondQuote = i;
287                     }
288                     // We've found both ends of an unquoted segment (could be whole challenge), search inside for the signature.
289                     if (i == challenge.Length - 1 || (challenge[i] == '\"' && firstQuote > secondQuote))
290                     {
291                         // see if the portion of challenge out of the quotes contains
292                         // the signature of the IAuthenticationModule
293                         if (i == challenge.Length - 1)
294                             firstQuote = challenge.Length;
295                         // unquoted segment is too small to hold a scheme name, ie: scheme param="value",a=""
296                         if (firstQuote < secondQuote + 3)
297                             continue;
298
299                         int checkstart = secondQuote + 1;
300                         int checkLength = firstQuote - secondQuote - 1;
301                         do
302                         {
303                             // Search for the next (partial match) occurance of the signature
304                             index = IndexOf(challenge, signature, checkstart, checkLength);
305
306                             if (index >= 0)
307                             {
308                                 // Verify the signature is a full scheme name match, not a partial match or a parameter name:
309                                 if ((index == 0 || challenge[index - 1] == ' ' || challenge[index - 1] == ',') &&
310                                     (index + signature.Length == challenge.Length || challenge[index + signature.Length] == ' ' || challenge[index + signature.Length] == ','))
311                                 {
312                                     break;
313                                 }
314                                 // Only a partial match / param name, but maybe there is another occurance of the signature later?
315                                 checkLength -= index - checkstart + 1;
316                                 checkstart = index + 1;
317                             }
318                         } while (index >= 0);
319                     }
320                 }
321             }
322             GlobalLog.Print("AuthenticationManager::FindSubstringNotInQuotes(" + challenge + ", " + signature + ")=" + index.ToString());
323             return index;
324         }
325
326         //
327         // Helper for FindSubstringNotInQuotes
328         // Find the FIRST possible index of a signature.
329         private static int IndexOf(string challenge, string lwrCaseSignature, int start, int count)
330         {
331             count += start + 1 - lwrCaseSignature.Length;
332             for (; start < count; ++start)
333             {
334                 int i = 0;
335                 for (; i < lwrCaseSignature.Length; ++i)
336                 {
337                     // force a challenge char to lowecase (safe assuming it works on trusted ASCII source)
338                     if ((challenge[start + i] | 0x20) != lwrCaseSignature[i])
339                         break;
340                 }
341                 if (i == lwrCaseSignature.Length)
342                     return start;
343             }
344             return -1;
345         }
346
347         //
348         // this method is called by the IAuthenticationModule implementations
349         // (mainly Digest) to safely find their list of parameters in a challenge.
350         // it returns the index of the first ',' that is not included in quotes,
351         // -1 is returned on error or end of string. on return offset contains the
352         // index of the first '=' that is not included in quotes, -1 if no '=' was found.
353         //
354         internal static int SplitNoQuotes(string challenge, ref int offset) 
355         {
356             // GlobalLog.Print("SplitNoQuotes([" + challenge + "], " + offset.ToString() + ")");
357             //
358             // save offset
359             //
360             int realOffset = offset;
361             //
362             // default is not found
363             //
364             offset = -1;
365
366             if (challenge != null && realOffset < challenge.Length)
367             {
368                 int firstQuote = -1, secondQuote = -1;
369
370                 for (int i = realOffset; i < challenge.Length; i++)
371                 {
372                     //
373                     // firstQuote>secondQuote means we are in a quoted string
374                     //
375                     if (firstQuote > secondQuote && challenge[i] == '\\' && i + 1 < challenge.Length && challenge[i + 1] == '\"')
376                     {
377                         //
378                         // skip <\"> when in a quoted string
379                         //
380                         i++;
381                     }
382                     else if (challenge[i] == '\"')
383                     {
384                         if (firstQuote <= secondQuote)
385                         {
386                             firstQuote = i;
387                         }
388                         else
389                         {
390                             secondQuote = i;
391                         }
392                     }
393                     else if (challenge[i] == '=' && firstQuote <= secondQuote && offset < 0)
394                     {
395                         offset = i;
396                     }
397                     else if (challenge[i] == ',' && firstQuote <= secondQuote)
398                     {
399                         return i;
400                     }
401                 }
402             }
403
404             return -1;
405         }
406
407 #if !FEATURE_PAL
408         internal static Authorization GetGroupAuthorization(
409             IAuthenticationModule thisModule, 
410             string token, 
411             bool finished, 
412             NTAuthentication authSession, 
413             bool shareAuthenticatedConnections, 
414             bool mutualAuth) 
415         {
416             return new Authorization(
417                     token,
418                     finished,
419                     (shareAuthenticatedConnections) ? null 
420                         : (thisModule.GetType().FullName + "/" + authSession.UniqueUserId),
421                     mutualAuth);
422         }
423 #endif // !FEATURE_PAL
424
425     } // class AuthenticationManager
426
427 } // namespace System.Net