Merge pull request #3066 from alexanderkyte/pedump_sgen
[mono.git] / mcs / class / referencesource / System / net / System / Net / CredentialCache.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="CredentialCache.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.Net.Sockets;
9     using System.Collections;
10     using System.Runtime.InteropServices;
11     using System.Security.Permissions;
12     using System.Globalization;
13
14     // More sophisticated password cache that stores multiple
15     // name-password pairs and associates these with host/realm
16     /// <devdoc>
17     ///    <para>Provides storage for multiple credentials.</para>
18     /// </devdoc>
19     public class CredentialCache : ICredentials, ICredentialsByHost, IEnumerable {
20
21     // fields
22
23         private Hashtable cache = new Hashtable();
24         private Hashtable cacheForHosts = new Hashtable();
25         internal int m_version;
26
27         private int m_NumbDefaultCredInCache = 0;
28
29         // [thread token optimization] The resulting counter of default credential resided in the cache.
30         internal bool IsDefaultInCache {
31             get {
32                 return m_NumbDefaultCredInCache != 0;
33             }
34         }
35
36     // constructors
37
38         /// <devdoc>
39         ///    <para>
40         ///       Initializes a new instance of the <see cref='System.Net.CredentialCache'/> class.
41         ///    </para>
42         /// </devdoc>
43         public CredentialCache() {
44         }
45
46     // properties
47
48     // methods
49
50         /// <devdoc>
51         /// <para>Adds a <see cref='System.Net.NetworkCredential'/>
52         /// instance to the credential cache.</para>
53         /// </devdoc>
54         // UEUE
55         public void Add(Uri uriPrefix, string authType, NetworkCredential cred) {
56             //
57             // parameter validation
58             //
59             if (uriPrefix==null) {
60                 throw new ArgumentNullException("uriPrefix");
61             }
62             if (authType==null) {
63                 throw new ArgumentNullException("authType");
64             }
65             if ((cred is SystemNetworkCredential)
66 #if !FEATURE_PAL
67                 && !((string.Compare(authType, NtlmClient.AuthType, StringComparison.OrdinalIgnoreCase)==0)
68                      || (DigestClient.WDigestAvailable && (string.Compare(authType, DigestClient.AuthType, StringComparison.OrdinalIgnoreCase)==0))
69                      || (string.Compare(authType, KerberosClient.AuthType, StringComparison.OrdinalIgnoreCase)==0)
70                      || (string.Compare(authType, NegotiateClient.AuthType, StringComparison.OrdinalIgnoreCase)==0))
71 #endif
72                 ) {
73                 throw new ArgumentException(SR.GetString(SR.net_nodefaultcreds, authType), "authType");
74             }
75
76             ++m_version;
77
78             CredentialKey key = new CredentialKey(uriPrefix, authType);
79
80             GlobalLog.Print("CredentialCache::Add() Adding key:[" + key.ToString() + "], cred:[" + cred.Domain + "],[" + cred.UserName + "]");
81
82             cache.Add(key, cred);
83             if (cred is SystemNetworkCredential) {
84                 ++m_NumbDefaultCredInCache;
85             }
86         }
87
88
89         public void Add(string host, int port, string authenticationType, NetworkCredential credential) {
90             //
91             // parameter validation
92             //
93             if (host==null) {
94                 throw new ArgumentNullException("host");
95             }
96
97             if (authenticationType==null) {
98                 throw new ArgumentNullException("authenticationType");
99             }
100             
101             if (host.Length == 0) {
102                 throw new ArgumentException(SR.GetString(SR.net_emptystringcall,"host"));
103             }
104
105             if (port < 0) {
106                 throw new ArgumentOutOfRangeException("port");
107             }
108             if ((credential is SystemNetworkCredential)
109 #if !FEATURE_PAL
110                 && !((string.Compare(authenticationType, NtlmClient.AuthType, StringComparison.OrdinalIgnoreCase)==0)
111                      || (DigestClient.WDigestAvailable && (string.Compare(authenticationType, DigestClient.AuthType, StringComparison.OrdinalIgnoreCase)==0))
112                      || (string.Compare(authenticationType, KerberosClient.AuthType, StringComparison.OrdinalIgnoreCase)==0)
113                      || (string.Compare(authenticationType, NegotiateClient.AuthType, StringComparison.OrdinalIgnoreCase)==0))
114 #endif
115                 ) {
116                 throw new ArgumentException(SR.GetString(SR.net_nodefaultcreds, authenticationType), "authenticationType");
117             }
118
119             ++m_version;
120
121             CredentialHostKey key = new CredentialHostKey(host,port, authenticationType);
122
123             GlobalLog.Print("CredentialCache::Add() Adding key:[" + key.ToString() + "], cred:[" + credential.Domain + "],[" + credential.UserName + "]");
124
125             cacheForHosts.Add(key, credential);
126             if (credential is SystemNetworkCredential) {
127                 ++m_NumbDefaultCredInCache;
128             }
129         }
130
131
132         /// <devdoc>
133         /// <para>Removes a <see cref='System.Net.NetworkCredential'/>
134         /// instance from the credential cache.</para>
135         /// </devdoc>
136         public void Remove(Uri uriPrefix, string authType) {
137             if (uriPrefix==null || authType==null) {
138                 // these couldn't possibly have been inserted into
139                 // the cache because of the test in Add()
140                 return;
141             }
142
143             ++m_version;
144
145             CredentialKey key = new CredentialKey(uriPrefix, authType);
146
147             GlobalLog.Print("CredentialCache::Remove() Removing key:[" + key.ToString() + "]");
148
149             if (cache[key] is SystemNetworkCredential) {
150                 --m_NumbDefaultCredInCache;
151             }
152             cache.Remove(key);
153         }
154
155
156         public void Remove(string host, int port, string authenticationType) {
157             if (host==null || authenticationType==null) {
158                 // these couldn't possibly have been inserted into
159                 // the cache because of the test in Add()
160                 return;
161             }
162
163             if (port < 0) {
164                 return;
165             }
166
167
168             ++m_version;
169
170             CredentialHostKey key = new CredentialHostKey(host, port, authenticationType);
171
172             GlobalLog.Print("CredentialCache::Remove() Removing key:[" + key.ToString() + "]");
173
174             if (cacheForHosts[key] is SystemNetworkCredential) {
175                 --m_NumbDefaultCredInCache;
176             }
177             cacheForHosts.Remove(key);
178         }
179
180         /// <devdoc>
181         ///    <para>
182         ///       Returns the <see cref='System.Net.NetworkCredential'/>
183         ///       instance associated with the supplied Uri and
184         ///       authentication type.
185         ///    </para>
186         /// </devdoc>
187         public NetworkCredential GetCredential(Uri uriPrefix, string authType) {
188             if (uriPrefix==null)
189                 throw new ArgumentNullException("uriPrefix");
190             if (authType==null)
191                 throw new ArgumentNullException("authType");
192             
193             GlobalLog.Print("CredentialCache::GetCredential(uriPrefix=\"" + uriPrefix + "\", authType=\"" + authType + "\")");
194
195             int longestMatchPrefix = -1;
196             NetworkCredential mostSpecificMatch = null;
197             IDictionaryEnumerator credEnum = cache.GetEnumerator();
198
199             //
200             // Enumerate through every credential in the cache
201             //
202
203             while (credEnum.MoveNext()) {
204
205                 CredentialKey key = (CredentialKey)credEnum.Key;
206
207                 //
208                 // Determine if this credential is applicable to the current Uri/AuthType
209                 //
210
211                 if (key.Match(uriPrefix, authType)) {
212
213                     int prefixLen = key.UriPrefixLength;
214
215                     //
216                     // Check if the match is better than the current-most-specific match
217                     //
218
219                     if (prefixLen > longestMatchPrefix) {
220
221                         //
222                         // Yes-- update the information about currently preferred match
223                         //
224
225                         longestMatchPrefix = prefixLen;
226                         mostSpecificMatch = (NetworkCredential)credEnum.Value;
227                     }
228                 }
229             }
230
231             GlobalLog.Print("CredentialCache::GetCredential returning " + ((mostSpecificMatch==null)?"null":"(" + mostSpecificMatch.UserName + ":" + mostSpecificMatch.Domain + ")"));
232
233             return mostSpecificMatch;
234         }
235
236
237         public NetworkCredential GetCredential(string host, int port, string authenticationType) {
238             if (host==null) {
239                 throw new ArgumentNullException("host");
240             }
241             if (authenticationType==null){
242                 throw new ArgumentNullException("authenticationType");
243             }
244             if (host.Length == 0) {
245                 throw new ArgumentException(SR.GetString(SR.net_emptystringcall, "host"));
246             }
247             if (port < 0) {
248                 throw new ArgumentOutOfRangeException("port");
249             }
250
251
252             GlobalLog.Print("CredentialCache::GetCredential(host=\"" + host + ":" + port.ToString() +"\", authenticationType=\"" + authenticationType + "\")");
253
254             NetworkCredential match = null;
255
256             IDictionaryEnumerator credEnum = cacheForHosts.GetEnumerator();
257
258             //
259             // Enumerate through every credential in the cache
260             //
261
262             while (credEnum.MoveNext()) {
263
264                 CredentialHostKey key = (CredentialHostKey)credEnum.Key;
265
266                 //
267                 // Determine if this credential is applicable to the current Uri/AuthType
268                 //
269
270                 if (key.Match(host, port, authenticationType)) {
271
272                         match = (NetworkCredential)credEnum.Value;
273                 }
274             }
275
276             GlobalLog.Print("CredentialCache::GetCredential returning " + ((match==null)?"null":"(" + match.UserName + ":" + match.Domain + ")"));
277             return match;
278         }
279
280
281
282         /// <devdoc>
283         ///    [To be supplied]
284         /// </devdoc>
285
286         //
287         // IEnumerable interface
288         //
289
290         public IEnumerator GetEnumerator() {
291             return new CredentialEnumerator(this, cache ,cacheForHosts, m_version);
292         }
293
294
295         /// <devdoc>
296         ///    <para>
297         ///       Gets
298         ///       the default system credentials from the <see cref='System.Net.CredentialCache'/>.
299         ///    </para>
300         /// </devdoc>
301         public static ICredentials DefaultCredentials {
302             get {
303                 //This check will not allow to use local user credentials at will.
304                 //Hence the username will not be exposed to the network
305 #if !DISABLE_CAS_USE
306                 new EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME").Demand();
307 #endif
308                 return SystemNetworkCredential.defaultCredential;
309             }
310         }
311         
312         public static NetworkCredential DefaultNetworkCredentials {
313             get {
314                 //This check will not allow to use local user credentials at will.
315                 //Hence the username will not be exposed to the network
316 #if !DISABLE_CAS_USE
317                 new EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME").Demand();
318 #endif
319                 return SystemNetworkCredential.defaultCredential;
320             }
321         }
322
323         private class CredentialEnumerator : IEnumerator {
324
325         // fields
326
327             private CredentialCache m_cache;
328             private ICredentials[] m_array;
329             private int m_index = -1;
330             private int m_version;
331
332         // constructors
333
334             internal CredentialEnumerator(CredentialCache cache, Hashtable table, Hashtable hostTable, int version) {
335                 m_cache = cache;
336                 m_array = new ICredentials[table.Count + hostTable.Count];
337                 table.Values.CopyTo(m_array, 0);
338                 hostTable.Values.CopyTo(m_array, table.Count);
339                 m_version = version;
340             }
341
342         // IEnumerator interface
343
344         // properties
345
346             object IEnumerator.Current {
347                 get {
348                     if (m_index < 0 || m_index >= m_array.Length) {
349                         throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumOpCantHappen));
350                     }
351                     if (m_version != m_cache.m_version) {
352                         throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumFailedVersion));
353                     }
354                     return m_array[m_index];
355                 }
356             }
357
358         // methods
359
360             bool IEnumerator.MoveNext() {
361                 if (m_version != m_cache.m_version) {
362                     throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumFailedVersion));
363                 }
364                 if (++m_index < m_array.Length) {
365                     return true;
366                 }
367                 m_index = m_array.Length;
368                 return false;
369             }
370
371             void IEnumerator.Reset() {
372                 m_index = -1;
373             }
374
375         } // class CredentialEnumerator
376
377
378     } // class CredentialCache
379
380
381
382     // Abstraction for credentials in password-based
383     // authentication schemes (basic, digest, NTLM, Kerberos)
384     // Note this is not applicable to public-key based
385     // systems such as SSL client authentication
386     // "Password" here may be the clear text password or it
387     // could be a one-way hash that is sufficient to
388     // authenticate, as in HTTP/1.1 digest.
389
390     //
391     // Object representing default credentials
392     //
393     internal class SystemNetworkCredential : NetworkCredential {
394         internal static readonly SystemNetworkCredential defaultCredential = new SystemNetworkCredential();
395
396         // We want reference equality to work.  Making this private is a good way to guarantee that.
397         private SystemNetworkCredential() :
398             base(string.Empty, string.Empty, string.Empty) {
399         }
400     }
401
402
403     internal class CredentialHostKey {
404
405         internal string Host;
406         internal string AuthenticationType;
407         internal int Port;
408
409         internal CredentialHostKey(string host, int port, string authenticationType) {
410             Host = host;
411             Port = port;
412             AuthenticationType = authenticationType;
413         }
414
415         internal bool Match(string host, int port, string authenticationType) {
416             if (host==null || authenticationType==null) {
417                 return false;
418             }
419             //
420             // If the protocols dont match this credential
421             // is not applicable for the given Uri
422             //
423             if (string.Compare(authenticationType, AuthenticationType, StringComparison.OrdinalIgnoreCase) != 0) {
424                 return false;
425             }
426             if (string.Compare(Host, host, StringComparison.OrdinalIgnoreCase ) != 0) {
427                 return false;
428             }
429             if (port != Port) {
430                 return false;
431             }
432
433             GlobalLog.Print("CredentialKey::Match(" + Host.ToString() + ":" + Port.ToString() +" & " + host.ToString() + ":" + port.ToString() + ")");
434             return true;
435         }
436
437
438         private int m_HashCode = 0;
439         private bool m_ComputedHashCode = false;
440         public override int GetHashCode() {
441             if (!m_ComputedHashCode) {
442                 //
443                 // compute HashCode on demand
444                 //
445
446                 m_HashCode = AuthenticationType.ToUpperInvariant().GetHashCode() + Host.ToUpperInvariant().GetHashCode() + Port.GetHashCode();
447                 m_ComputedHashCode = true;
448             }
449             return m_HashCode;
450         }
451
452         public override bool Equals(object comparand) {
453             CredentialHostKey comparedCredentialKey = comparand as CredentialHostKey;
454
455             if (comparand==null) {
456                 //
457                 // this covers also the compared==null case
458                 //
459                 return false;
460             }
461
462             bool equals =
463                 (string.Compare(AuthenticationType, comparedCredentialKey.AuthenticationType, StringComparison.OrdinalIgnoreCase ) == 0) &&
464                 (string.Compare(Host, comparedCredentialKey.Host, StringComparison.OrdinalIgnoreCase ) == 0) &&
465                 Port == comparedCredentialKey.Port;
466
467             GlobalLog.Print("CredentialKey::Equals(" + ToString() + ", " + comparedCredentialKey.ToString() + ") returns " + equals.ToString());
468
469             return equals;
470         }
471
472         public override string ToString() {
473             return "[" + Host.Length.ToString(NumberFormatInfo.InvariantInfo) + "]:" + Host + ":" + Port.ToString(NumberFormatInfo.InvariantInfo) + ":" + ValidationHelper.ToString(AuthenticationType);
474         }
475
476     } // class CredentialKey
477
478
479     internal class CredentialKey {
480
481         internal Uri UriPrefix;
482         internal int UriPrefixLength = -1;
483         internal string AuthenticationType;
484
485         internal CredentialKey(Uri uriPrefix, string authenticationType) {
486             UriPrefix = uriPrefix;
487             UriPrefixLength = UriPrefix.ToString().Length;
488             AuthenticationType = authenticationType;
489         }
490
491         internal bool Match(Uri uri, string authenticationType) {
492             if (uri==null || authenticationType==null) {
493                 return false;
494             }
495             //
496             // If the protocols dont match this credential
497             // is not applicable for the given Uri
498             //
499             if (string.Compare(authenticationType, AuthenticationType, StringComparison.OrdinalIgnoreCase) != 0) {
500                 return false;
501             }
502
503             GlobalLog.Print("CredentialKey::Match(" + UriPrefix.ToString() + " & " + uri.ToString() + ")");
504
505             return IsPrefix(uri, UriPrefix);
506         }
507         //
508         // IsPrefix (Uri)
509         //
510         //  Determines whether <prefixUri> is a prefix of this URI. A prefix
511         //  match is defined as:
512         //
513         //      scheme match
514         //      + host match
515         //      + port match, if any
516         //      + <prefix> path is a prefix of <URI> path, if any
517         //
518         // Returns:
519         //  True if <prefixUri> is a prefix of this URI
520         //
521         internal bool IsPrefix(Uri uri, Uri prefixUri) {
522
523             if (prefixUri.Scheme != uri.Scheme || prefixUri.Host != uri.Host || prefixUri.Port != uri.Port)
524                 return false;
525
526             int prefixLen = prefixUri.AbsolutePath.LastIndexOf('/');
527             if (prefixLen > uri.AbsolutePath.LastIndexOf('/'))
528                 return false;
529
530             return String.Compare(uri.AbsolutePath, 0, prefixUri.AbsolutePath, 0, prefixLen, StringComparison.OrdinalIgnoreCase ) == 0;
531         }
532
533         private int m_HashCode = 0;
534         private bool m_ComputedHashCode = false;
535         public override int GetHashCode() {
536             if (!m_ComputedHashCode) {
537                 //
538                 // compute HashCode on demand
539                 //
540
541                 m_HashCode = AuthenticationType.ToUpperInvariant().GetHashCode() + UriPrefixLength + UriPrefix.GetHashCode();
542                 m_ComputedHashCode = true;
543             }
544             return m_HashCode;
545         }
546
547         public override bool Equals(object comparand) {
548             CredentialKey comparedCredentialKey = comparand as CredentialKey;
549
550             if (comparand==null) {
551                 //
552                 // this covers also the compared==null case
553                 //
554                 return false;
555             }
556
557             bool equals =
558                 (string.Compare(AuthenticationType, comparedCredentialKey.AuthenticationType, StringComparison.OrdinalIgnoreCase ) == 0) &&
559                 UriPrefix.Equals(comparedCredentialKey.UriPrefix);
560
561             GlobalLog.Print("CredentialKey::Equals(" + ToString() + ", " + comparedCredentialKey.ToString() + ") returns " + equals.ToString());
562
563             return equals;
564         }
565
566         public override string ToString() {
567             return "[" + UriPrefixLength.ToString(NumberFormatInfo.InvariantInfo) + "]:" + ValidationHelper.ToString(UriPrefix) + ":" + ValidationHelper.ToString(AuthenticationType);
568         }
569
570     } // class CredentialKey
571
572
573 } // namespace System.Net