Merge pull request #3769 from evincarofautumn/fix-verify-before-allocs
[mono.git] / mcs / class / referencesource / System / net / System / Net / _AuthenticationManagerBase.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="AuthenticationManagerBase.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     internal abstract class AuthenticationManagerBase : IAuthenticationManager
24     {
25         private static volatile ICredentialPolicy s_ICredentialPolicy;
26         private static SpnDictionary m_SpnDictionary = new SpnDictionary();
27
28         private static TriState s_OSSupportsExtendedProtection = TriState.Unspecified;
29         private static TriState s_SspSupportsExtendedProtection = TriState.Unspecified;
30
31         public ICredentialPolicy CredentialPolicy
32         {
33             get
34             {
35                 return s_ICredentialPolicy;
36             }
37             set
38             {
39                 s_ICredentialPolicy = value;
40             }
41         }
42
43         public virtual void EnsureConfigLoaded()
44         {
45             // No-op: performed at object creation.
46         }
47
48         public StringDictionary CustomTargetNameDictionary
49         {
50             get { return m_SpnDictionary; }
51         }
52         //
53         // This will give access to some internal methods
54         //
55         public SpnDictionary SpnDictionary
56         {
57             get { return m_SpnDictionary; }
58         }
59
60         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
61         public bool OSSupportsExtendedProtection
62         {
63
64             get
65             {
66
67                 if (s_OSSupportsExtendedProtection == TriState.Unspecified)
68                 {
69                     if (ComNetOS.IsWin7orLater)
70                     {
71                         s_OSSupportsExtendedProtection = TriState.True;
72                     }
73                     else
74                     {
75                         if (SspSupportsExtendedProtection)
76                         {
77                             // EP is considered supported only if both SSPs and http.sys support CBT/EP. 
78                             // We don't support scenarios where e.g. only SSPs support CBT. In such cases 
79                             // the customer needs to patch also http.sys (even if he may not use it).
80                             if (UnsafeNclNativeMethods.HttpApi.ExtendedProtectionSupported)
81                             {
82                                 s_OSSupportsExtendedProtection = TriState.True;
83                             }
84                             else
85                             {
86                                 s_OSSupportsExtendedProtection = TriState.False;
87                             }
88                         }
89                         else
90                         {
91                             s_OSSupportsExtendedProtection = TriState.False;
92                         }
93                     }
94                 }
95
96                 return (s_OSSupportsExtendedProtection == TriState.True);
97             }
98         }
99
100         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
101         public bool SspSupportsExtendedProtection
102         {
103
104             get
105             {
106
107                 if (s_SspSupportsExtendedProtection == TriState.Unspecified)
108                 {
109                     if (ComNetOS.IsWin7orLater)
110                     {
111                         s_SspSupportsExtendedProtection = TriState.True;
112                     }
113                     else
114                     {
115                         // Perform a loopback NTLM authentication to determine whether the underlying OS supports 
116                         // extended protection
117                         ContextFlags clientFlags = ContextFlags.Connection | ContextFlags.InitIdentify;
118
119                         NTAuthentication client = new NTAuthentication(false, NtlmClient.AuthType,
120                             SystemNetworkCredential.defaultCredential, "http/localhost", clientFlags, null);
121                         try
122                         {
123
124                             NTAuthentication server = new NTAuthentication(true, NtlmClient.AuthType,
125                                 SystemNetworkCredential.defaultCredential, null, ContextFlags.Connection, null);
126                             try
127                             {
128
129                                 SecurityStatus status;
130                                 byte[] blob = null;
131
132                                 while (!server.IsCompleted)
133                                 {
134                                     blob = client.GetOutgoingBlob(blob, true, out status);
135                                     blob = server.GetOutgoingBlob(blob, true, out status);
136                                 }
137
138                                 if (server.OSSupportsExtendedProtection)
139                                 {
140                                     s_SspSupportsExtendedProtection = TriState.True;
141                                 }
142                                 else
143                                 {
144                                     if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_ssp_dont_support_cbt));
145                                     s_SspSupportsExtendedProtection = TriState.False;
146                                 }
147                             }
148                             finally
149                             {
150                                 server.CloseContext();
151                             }
152                         }
153                         finally
154                         {
155                             client.CloseContext();
156                         }
157                     }
158                 }
159
160                 return (s_SspSupportsExtendedProtection == TriState.True);
161             }
162         }
163
164         /// <devdoc>
165         ///    <para>Call each registered authentication module to determine the first module that
166         ///       can respond to the authentication request.</para>
167         /// </devdoc>
168         public abstract Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials);
169
170         // These four authentication modules require a Channel Binding Token to be able to preauthenticate over https.
171         // After a successful authentication, they will cache the CBT used on the ServicePoint.  In order to PreAuthenticate,
172         // they require that a CBT has previously been cached.  Any other module should be allowed to try preauthentication
173         // without a cached CBT
174 #if DEBUG
175         // This method is only called as part of an assert
176         protected static bool ModuleRequiresChannelBinding(IAuthenticationModule authenticationModule)
177         {
178             return (authenticationModule is NtlmClient || authenticationModule is KerberosClient ||
179                     authenticationModule is NegotiateClient || authenticationModule is DigestClient);
180         }
181 #endif
182
183         /// <devdoc>
184         ///    <para>Pre-authenticates a request.</para>
185         /// </devdoc>
186         public abstract Authorization PreAuthenticate(WebRequest request, ICredentials credentials);
187
188         /// <devdoc>
189         ///    <para>Registers an authentication module with the authentication manager.</para>
190         /// </devdoc>
191         public abstract void Register(IAuthenticationModule authenticationModule);
192
193         /// <devdoc>
194         ///    <para>Unregisters authentication modules for an authentication scheme.</para>
195         /// </devdoc>
196         public abstract void Unregister(IAuthenticationModule authenticationModule);
197         /// <devdoc>
198         ///    <para>Unregisters authentication modules for an authentication scheme.</para>
199         /// </devdoc>
200         public abstract void Unregister(string authenticationScheme);
201
202         /// <devdoc>
203         ///    <para>
204         ///       Returns a list of registered authentication modules.
205         ///    </para>
206         /// </devdoc>
207         public abstract IEnumerator RegisteredModules
208         {
209             get;
210         }
211
212         /// <devdoc>
213         ///    <para>
214         ///       Binds an authentication response to a request for pre-authentication.
215         ///    </para>
216         /// </devdoc>
217         // Create binding between an authorization response and the module
218         // generating that response
219         // This association is used for deciding which module to invoke
220         // for preauthentication purposes
221         public abstract void BindModule(Uri uri, Authorization response, IAuthenticationModule module);
222
223         // This function returns a prefix of the given absolute Uri
224         // which will be used for associating authentication information
225         // The purpose is to associate the module-binding not with a single
226         // Uri but some collection generalizing that Uri to the loosely-defined
227         // notion of "protection realm"
228         protected static string generalize(Uri location)
229         {
230             string completeUri = location.GetComponents(UriComponents.AbsoluteUri
231                 & ~(UriComponents.Query | UriComponents.Fragment), UriFormat.UriEscaped);
232             int lastFwdSlash = completeUri.LastIndexOf('/');
233             if (lastFwdSlash < 0)
234             {
235                 return completeUri;
236             }
237             return completeUri.Substring(0, lastFwdSlash + 1);
238         }
239     }; // class AuthenticationManagerBase
240
241 } // namespace System.Net