X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem%2FSystem.Net%2FDigestClient.cs;h=ef7438277218e42d1b9353a98e009f801a8afd77;hb=9d17307d3bbc1233e8ccce1173fe4497e64504f6;hp=aa62fece4d292e79152aabd634e4f142aa88a2f5;hpb=1ccd257868d805a2d892388384ff8b7a24ec528b;p=mono.git diff --git a/mcs/class/System/System.Net/DigestClient.cs b/mcs/class/System/System.Net/DigestClient.cs index aa62fece4d2..ef743827721 100644 --- a/mcs/class/System/System.Net/DigestClient.cs +++ b/mcs/class/System/System.Net/DigestClient.cs @@ -14,6 +14,27 @@ // http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html // +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + using System; using System.Collections; using System.Collections.Specialized; @@ -66,7 +87,7 @@ namespace System.Net public string QOP { get { return values [4]; } } - + public bool Parse () { if (!header.ToLower ().StartsWith ("digest ")) @@ -108,15 +129,6 @@ namespace System.Net pos--; } - void SkipNonWhitespace () - { - char c = 'a'; - while (pos < length && c != ' ' && c != '\t' && c != '\r' && c != '\n') { - c = header [pos++]; - } - pos--; - } - string GetKey () { SkipWhitespace (); @@ -142,13 +154,31 @@ namespace System.Net return false; SkipWhitespace (); - if (pos + 1 >= length || header [pos++] != '"') + // note: Apache doesn't use " in all case (like algorithm) + if (pos + 1 >= length) return false; + bool useQuote = false; + if (header [pos] == '"') { + pos++; + useQuote = true; + } + int beginQ = pos; - pos = header.IndexOf ('"', pos); - if (pos == -1) - return false; + if (useQuote) { + pos = header.IndexOf ('"', pos); + if (pos == -1) + return false; + } else { + do { + char c = header [pos]; + if (c == ',' || c == ' ' || c == '\t' || c == '\r' || c == '\n') + break; + } while (++pos < length); + + if (pos >= length && beginQ == pos) + return false; + } value = header.Substring (beginQ, pos - beginQ); pos += 2; @@ -159,6 +189,7 @@ namespace System.Net class DigestSession { static RandomNumberGenerator rng; + DateTime lastUse; static DigestSession () { @@ -173,6 +204,7 @@ namespace System.Net public DigestSession () { _nc = 1; + lastUse = DateTime.Now; } public string Algorithm { @@ -213,7 +245,6 @@ namespace System.Net { parser = new DigestHeaderParser (challenge); if (!parser.Parse ()) { - Console.WriteLine ("Parser"); return false; } @@ -248,7 +279,7 @@ namespace System.Net private string HA2 (HttpWebRequest webRequest) { - string ha2 = String.Format ("{0}:{1}", webRequest.Method, webRequest.RequestUri.AbsolutePath); + string ha2 = String.Format ("{0}:{1}", webRequest.Method, webRequest.RequestUri.PathAndQuery); if (QOP == "auth-int") { // TODO // ha2 += String.Format (":{0}", hentity); @@ -260,7 +291,7 @@ namespace System.Net { string response = String.Format ("{0}:{1}:", HA1 (username, password), Nonce); if (QOP != null) - response += String.Format ("{0}:{1}:{2}:", _nc.ToString ("x8"), CNonce, QOP); + response += String.Format ("{0}:{1}:{2}:", _nc.ToString ("X8"), CNonce, QOP); response += HA2 (webRequest); return HashToHexString (response); } @@ -274,7 +305,11 @@ namespace System.Net if (request == null) return null; + lastUse = DateTime.Now; NetworkCredential cred = credentials.GetCredential (request.RequestUri, "digest"); + if (cred == null) + return null; + string userName = cred.UserName; if (userName == null || userName == "") return null; @@ -287,11 +322,15 @@ namespace System.Net auth.AppendFormat ("nonce=\"{0}\", ", Nonce); auth.AppendFormat ("uri=\"{0}\", ", request.Address.PathAndQuery); - if (QOP != null) // quality of protection (server decision) - auth.AppendFormat ("qop=\"{0}\", ", QOP); - - if (Algorithm != null) // hash algorithm (only MD5 in RFC2617) + if (Algorithm != null) { // hash algorithm (only MD5 in RFC2617) auth.AppendFormat ("algorithm=\"{0}\", ", Algorithm); + } + + auth.AppendFormat ("response=\"{0}\", ", Response (userName, password, request)); + + if (QOP != null) { // quality of protection (server decision) + auth.AppendFormat ("qop=\"{0}\", ", QOP); + } lock (this) { // _nc MUST NOT change from here... @@ -303,29 +342,62 @@ namespace System.Net // until here, now _nc can change } - if (QOP != null) // opaque value from the client + if (CNonce != null) // opaque value from the client auth.AppendFormat ("cnonce=\"{0}\", ", CNonce); if (Opaque != null) // exact same opaque value as received from server auth.AppendFormat ("opaque=\"{0}\", ", Opaque); - auth.AppendFormat ("response=\"{0}\"", Response (userName, password, request)); + auth.Length -= 2; // remove ", " return new Authorization (auth.ToString ()); } + + public DateTime LastUse { + get { return lastUse; } + } } class DigestClient : IAuthenticationModule { - static Hashtable cache; // cache entries by nonce + static readonly Hashtable cache = Hashtable.Synchronized (new Hashtable ()); + + static Hashtable Cache { + get { + lock (cache.SyncRoot) { + CheckExpired (cache.Count); + } + + return cache; + } + } - static DigestClient () + static void CheckExpired (int count) { - cache = Hashtable.Synchronized (new Hashtable ()); + if (count < 10) + return; + + DateTime t = DateTime.MaxValue; + DateTime now = DateTime.Now; + ArrayList list = null; + foreach (int key in cache.Keys) { + DigestSession elem = (DigestSession) cache [key]; + if (elem.LastUse < t && + (elem.LastUse - now).Ticks > TimeSpan.TicksPerMinute * 10) { + t = elem.LastUse; + if (list == null) + list = new ArrayList (); + + list.Add (key); + } + } + + if (list != null) { + foreach (int k in list) + cache.Remove (k); + } } - - public DigestClient () {} - + // IAuthenticationModule public Authorization Authenticate (string challenge, WebRequest webRequest, ICredentials credentials) @@ -334,14 +406,15 @@ namespace System.Net return null; string header = challenge.Trim (); - if (!header.ToLower ().IndexOf ("digest")) + if (header.ToLower ().IndexOf ("digest") == -1) return null; HttpWebRequest request = webRequest as HttpWebRequest; if (request == null) return null; - DigestSession ds = (DigestSession) cache [request.Address]; + int hashcode = request.Address.GetHashCode () ^ credentials.GetHashCode (); + DigestSession ds = (DigestSession) Cache [hashcode]; bool addDS = (ds == null); if (addDS) ds = new DigestSession (); @@ -350,7 +423,7 @@ namespace System.Net return null; if (addDS) - cache.Add (request.Address, ds); + Cache.Add (hashcode, ds); return ds.Authenticate (webRequest, credentials); } @@ -361,8 +434,11 @@ namespace System.Net if (request == null) return null; - // check cache for URI - DigestSession ds = (DigestSession) cache [request.Address]; + if (credentials == null) + return null; + + int hashcode = request.Address.GetHashCode () ^ credentials.GetHashCode (); + DigestSession ds = (DigestSession) Cache [hashcode]; if (ds == null) return null;