// // this method computes the response-value according to the // rules described in RFC2831 section 2.1.2.1 // private static string responseValue(HttpDigestChallenge challenge, string username, string password) { string secretString = computeSecret(challenge, username, password); if (secretString == null) { return(null); } string dataString = computeData(challenge); if (dataString == null) { return(null); } string secret = hashString(secretString, challenge.MD5provider); string hexMD2 = hashString(dataString, challenge.MD5provider); string data = challenge.Nonce + ":" + (challenge.QopPresent ? challenge.NonceCount.ToString("x8") + ":" + challenge.ClientNonce + ":" + challenge.QualityOfProtection + ":" + hexMD2 : hexMD2); return(hashString(secret + ":" + data, challenge.MD5provider)); }
private static string createUpgradedNonce(HttpDigestChallenge digestChallenge) { string s = digestChallenge.ServiceName + ":" + digestChallenge.ChannelBinding; byte[] rawbytes = digestChallenge.MD5provider.ComputeHash(Encoding.ASCII.GetBytes(s)); return("+Upgraded+v1" + hexEncode(rawbytes) + createNonce(0x20)); }
private static string computeData(HttpDigestChallenge challenge) { // we only support "auth" QualityOfProtection. if it's not what the server wants we'll throw: // the case in which the server sends no qop directive defaults to "auth" QualityOfProtection. if (challenge.QopPresent) { int index = 0; while (index >= 0) { // find the next occurence of "auth" index = challenge.QualityOfProtection.IndexOf(SupportedQuality, index); if (index < 0) { throw new NotSupportedException(SR.GetString(SR.net_QOPNotSupportedException, challenge.QualityOfProtection)); } // if it's a whole word we're done if ((index == 0 || ValidSeparator.IndexOf(challenge.QualityOfProtection[index - 1]) >= 0) && (index + SupportedQuality.Length == challenge.QualityOfProtection.Length || ValidSeparator.IndexOf(challenge.QualityOfProtection[index + SupportedQuality.Length]) >= 0)) { break; } index += SupportedQuality.Length; } } return(challenge.Method + ":" + challenge.Uri); }
private static string RefineDigestChallenge(string challenge, int index) { string str = null; int num4; if ((challenge == null) || (index >= challenge.Length)) { throw new ArgumentOutOfRangeException("challenge", challenge); } int startIndex = index + SignatureSize; if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { index = -1; } if ((index < 0) || (challenge.Length <= startIndex)) { throw new ArgumentOutOfRangeException("challenge", challenge); } str = challenge.Substring(startIndex); int num3 = 0; bool flag = true; HttpDigestChallenge challenge2 = new HttpDigestChallenge(); Label_0070: num4 = num3; index = AuthenticationManager.SplitNoQuotes(str, ref num4); if (num4 >= 0) { string str3; string name = str.Substring(num3, num4 - num3); if (index < 0) { str3 = HttpDigest.unquote(str.Substring(num4 + 1)); } else { str3 = HttpDigest.unquote(str.Substring(num4 + 1, (index - num4) - 1)); } flag = challenge2.defineAttribute(name, str3); if ((index >= 0) && flag) { num3 = ++index; goto Label_0070; } } if ((!flag || (num4 < 0)) && (num3 < str.Length)) { str = (num3 > 0) ? str.Substring(0, num3 - 1) : ""; } return(str); }
public HttpDigestChallenge CopyAndIncrementNonce() { HttpDigestChallenge challengeCopy = null; lock (this) { challengeCopy = this.MemberwiseClone() as HttpDigestChallenge; ++NonceCount; } challengeCopy.MD5provider = new MD5CryptoServiceProvider(); return(challengeCopy); }
internal HttpDigestChallenge CopyAndIncrementNonce() { HttpDigestChallenge challenge = null; lock (this) { challenge = base.MemberwiseClone() as HttpDigestChallenge; this.NonceCount++; } challenge.MD5provider = new MD5CryptoServiceProvider(); return(challenge); }
private static string computeSecret(HttpDigestChallenge challenge, string username, string password) { if (challenge.Algorithm == null || string.Compare(challenge.Algorithm, "md5", true, CultureInfo.InvariantCulture) == 0) { return(username + ":" + challenge.Realm + ":" + password); } else if (string.Compare(challenge.Algorithm, "md5-sess", true, CultureInfo.InvariantCulture) == 0) { return(hashString(username + ":" + challenge.Realm + ":" + password, challenge.MD5provider) + ":" + challenge.Nonce + ":" + challenge.ClientNonce); } throw new NotSupportedException(SR.GetString(SR.net_HashAlgorithmNotSupportedException, challenge.Algorithm)); }
private static string computeSecret(HttpDigestChallenge challenge, string username, string password) { if ((challenge.Algorithm == null) || (string.Compare(challenge.Algorithm, "md5", StringComparison.OrdinalIgnoreCase) == 0)) { return(username + ":" + challenge.Realm + ":" + password); } if (string.Compare(challenge.Algorithm, "md5-sess", StringComparison.OrdinalIgnoreCase) == 0) { return(hashString(username + ":" + challenge.Realm + ":" + password, challenge.MD5provider) + ":" + challenge.Nonce + ":" + challenge.ClientNonce); } if (Logging.On) { Logging.PrintError(Logging.Web, SR.GetString("net_log_digest_hash_algorithm_not_supported", new object[] { challenge.Algorithm })); } return(null); }
public Authorization PreAuthenticate(WebRequest webRequest, ICredentials credentials) { GlobalLog.Print("DigestClient::PreAuthenticate()"); #if XP_WDIGEST if (ComNetOS.IsPostWin2K) { return(XPDigestClient.PreAuthenticate(webRequest, credentials)); } #endif // #if XP_WDIGEST GlobalLog.Assert(credentials != null, "DigestClient::PreAuthenticate() credentials==null", ""); if (credentials == null || credentials is SystemNetworkCredential) { return(null); } HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; GlobalLog.Assert(httpWebRequest != null, "DigestClient::PreAuthenticate() httpWebRequest==null", ""); if (httpWebRequest == null) { return(null); } HttpDigestChallenge storedHDC = (HttpDigestChallenge)challengeCache.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri); if (storedHDC == null) { return(null); } HttpDigestChallenge modifiedHDC = storedHDC.CopyAndIncrementNonce(); modifiedHDC.HostName = httpWebRequest.ChallengedUri.Host; modifiedHDC.Method = httpWebRequest.CurrentMethod; // Consider: // I have also tried PathAndQuery against both IIS 5.0 and IIS 6.0 servers. // it didn't make a difference. PathAndQuery is a more complete piece of information // investigate with Kevin Damour if WDigest.dll wants the quesry string or not. modifiedHDC.Uri = httpWebRequest.Address.AbsolutePath; modifiedHDC.ChallengedUri = httpWebRequest.ChallengedUri; Authorization digestResponse = HttpDigest.Authenticate(modifiedHDC, credentials); return(digestResponse); }
internal static bool CheckQOP(HttpDigestChallenge challenge) { if (challenge.QopPresent) { for (int i = 0; i >= 0; i += "auth".Length) { i = challenge.QualityOfProtection.IndexOf("auth", i); if (i < 0) { return false; } if (((i == 0) || (", \"'\t\r\n".IndexOf(challenge.QualityOfProtection[i - 1]) >= 0)) && (((i + "auth".Length) == challenge.QualityOfProtection.Length) || (", \"'\t\r\n".IndexOf(challenge.QualityOfProtection[i + "auth".Length]) >= 0))) { break; } } } return true; }
internal static bool CheckQOP(HttpDigestChallenge challenge) { if (challenge.QopPresent) { for (int i = 0; i >= 0; i += "auth".Length) { i = challenge.QualityOfProtection.IndexOf("auth", i); if (i < 0) { return(false); } if (((i == 0) || (", \"'\t\r\n".IndexOf(challenge.QualityOfProtection[i - 1]) >= 0)) && (((i + "auth".Length) == challenge.QualityOfProtection.Length) || (", \"'\t\r\n".IndexOf(challenge.QualityOfProtection[i + "auth".Length]) >= 0))) { break; } } } return(true); }
public bool Update(string challenge, WebRequest webRequest) { HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; if (httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this) != null) { return(this.XPUpdate(challenge, httpWebRequest)); } if (httpWebRequest.ResponseStatusCode != httpWebRequest.CurrentAuthenticationState.StatusCodeMatch) { ChannelBinding channelBinding = null; if (httpWebRequest.CurrentAuthenticationState.TransportContext != null) { channelBinding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } httpWebRequest.ServicePoint.SetCachedChannelBinding(httpWebRequest.ChallengedUri, channelBinding); return(true); } int startingPoint = (challenge == null) ? -1 : AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (startingPoint < 0) { return(true); } int startIndex = startingPoint + SignatureSize; if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { startingPoint = -1; } if ((startingPoint >= 0) && (challenge.Length > startIndex)) { challenge.Substring(startIndex); } HttpDigestChallenge challenge2 = HttpDigest.Interpret(challenge, startingPoint, httpWebRequest); return((challenge2 == null) || !challenge2.Stale); }
private static string responseValue(HttpDigestChallenge challenge, string username, string password) { string myString = computeSecret(challenge, username, password); if (myString == null) { return(null); } string str2 = challenge.Method + ":" + challenge.Uri; if (str2 == null) { return(null); } string str3 = hashString(myString, challenge.MD5provider); string str4 = hashString(str2, challenge.MD5provider); string str5 = challenge.Nonce + ":" + (challenge.QopPresent ? (challenge.NonceCount.ToString("x8", NumberFormatInfo.InvariantInfo) + ":" + challenge.ClientNonce + ":auth:" + str4) : str4); return(hashString(str3 + ":" + str5, challenge.MD5provider)); }
// // this method parses the challenge and breaks it into the // fundamental pieces that Digest defines and understands // internal static HttpDigestChallenge Interpret(string challenge, int startingPoint, HttpWebRequest httpWebRequest) { HttpDigestChallenge digestChallenge = new HttpDigestChallenge(); digestChallenge.SetFromRequest(httpWebRequest); // // define the part of the challenge we really care about // startingPoint = startingPoint==-1 ? 0 : startingPoint + DigestClient.SignatureSize; bool valid; int start, offset, index; string name, value; // forst time parse looking for a charset="utf-8" directive // not too bad, IIS 6.0, by default, sends this as the first directive. // if the server does not send this we'll end up parsing twice. start = startingPoint; for (;;) { offset = start; index = AuthenticationManager.SplitNoQuotes(challenge, ref offset); if (offset<0) { break; } name = challenge.Substring(start, offset-start); if (string.Compare(name, DA_charset, StringComparison.OrdinalIgnoreCase)==0) { if (index<0) { value = unquote(challenge.Substring(offset+1)); } else { value = unquote(challenge.Substring(offset+1, index-offset-1)); } GlobalLog.Print("HttpDigest::Interpret() server provided a hint to use [" + value + "] encoding"); if (string.Compare(value, "utf-8", StringComparison.OrdinalIgnoreCase)==0) { digestChallenge.UTF8Charset = true; break; } } if (index<0) { break; } start = ++index; } // this time go through the directives, parse them and call defineAttribute() start = startingPoint; for (;;) { offset = start; index = AuthenticationManager.SplitNoQuotes(challenge, ref offset); GlobalLog.Print("HttpDigest::Interpret() SplitNoQuotes() returning index:" + index.ToString() + " offset:" + offset.ToString()); if (offset<0) { break; } name = challenge.Substring(start, offset-start); if (index<0) { value = unquote(challenge.Substring(offset+1)); } else { value = unquote(challenge.Substring(offset+1, index-offset-1)); } if (digestChallenge.UTF8Charset) { bool isAscii = true; for (int i=0; i<value.Length; i++) { if (value[i]>(char)0x7F) { isAscii = false; break; } } if (!isAscii) { GlobalLog.Print("HttpDigest::Interpret() UTF8 decoding required value:[" + value + "]"); byte[] bytes = new byte[value.Length]; for (int i=0; i<value.Length; i++) { bytes[i] = (byte)value[i]; } value = Encoding.UTF8.GetString(bytes); GlobalLog.Print("HttpDigest::Interpret() UTF8 decoded value:[" + value + "]"); } else { GlobalLog.Print("HttpDigest::Interpret() no need for special encoding"); } } valid = digestChallenge.defineAttribute(name, value); GlobalLog.Print("HttpDigest::Interpret() defineAttribute(" + name + ", " + value + ") returns " + valid.ToString()); if (index<0 || !valid) { break; } start = ++index; } // We must absolutely have a nonce for Digest to work. if (digestChallenge.Nonce == null) { if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_digest_requires_nonce)); return null; } return digestChallenge; }
// // Extract digest relevant part from a raw server blob // private static string RefineDigestChallenge(string challenge, int index) { string incoming = null; Debug.Assert(challenge != null); Debug.Assert(index >= 0 && index < challenge.Length); int blobBegin = index + SignatureSize; // // there may be multiple challenges. If the next character after the // package name is not a comma then it is challenge data // if (challenge.Length > blobBegin && challenge[blobBegin] != ',') { ++blobBegin; } else { index = -1; } if (index >= 0 && challenge.Length > blobBegin) { incoming = challenge.Substring(blobBegin); } else { Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_auth_invalid_challenge, DigestClient.AuthType)); return String.Empty; // Error, no valid digest challenge, no further processing required } // now make sure there's nothing at the end of the challenge that is not part of the digest challenge // this would happen if I have a Digest challenge followed by another challenge like ",NTLM,Negotiate" // use this DCR when avaialble to do this without parsing: // 762116 2 WDigest should ignore directives that do not have a value int startingPoint = 0; int start = startingPoint; int offset; bool valid = true; string name, value; HttpDigestChallenge digestChallenge = new HttpDigestChallenge(); for (;;) { offset = start; index = AuthenticationManager.SplitNoQuotes(incoming, ref offset); GlobalLog.Print("DigestClient::XPDoAuthenticate() SplitNoQuotes() returning index:" + index + " offset:" + offset); if (offset<0) { break; } name = incoming.Substring(start, offset-start); if (index<0) { value = HttpDigest.unquote(incoming.Substring(offset+1)); } else { value = HttpDigest.unquote(incoming.Substring(offset+1, index-offset-1)); } valid = digestChallenge.defineAttribute(name, value); GlobalLog.Print("DigestClient::XPDoAuthenticate() defineAttribute(" + name + ", " + value + ") returns " + valid); if (index<0 || !valid) { break; } start = ++index; } GlobalLog.Print("DigestClient::XPDoAuthenticate() start:" + start + " offset:" + offset + " index:" + index + " valid:" + valid + " incoming.Length:" + incoming.Length + " incoming:" + incoming); if ((!valid || offset<0) && start<incoming.Length) { incoming = start > 0 ? incoming.Substring(0, start-1) : ""; // First parameter might have been invalid, leaving start at 0 } return incoming; }
internal static bool CheckQOP(HttpDigestChallenge challenge) { // our internal implementatoin only support "auth" QualityOfProtection. // if it's not what the server wants we'll have to throw: // the case in which the server sends no qop directive we default to "auth" if (challenge.QopPresent) { int index = 0; while (index>=0) { // find the next occurence of "auth" index = challenge.QualityOfProtection.IndexOf(HttpDigest.SupportedQuality, index); if (index<0) { return false; } // if it's a whole word we're done if ((index==0 || HttpDigest.ValidSeparator.IndexOf(challenge.QualityOfProtection[index - 1])>=0) && (index+HttpDigest.SupportedQuality.Length==challenge.QualityOfProtection.Length || HttpDigest.ValidSeparator.IndexOf(challenge.QualityOfProtection[index + HttpDigest.SupportedQuality.Length])>=0) ) { break; } index += HttpDigest.SupportedQuality.Length; } } return true; }
private static string createUpgradedNonce(HttpDigestChallenge digestChallenge) { string hashMe = digestChallenge.ServiceName + ":" + digestChallenge.ChannelBinding; byte[] hash = digestChallenge.MD5provider.ComputeHash(Encoding.ASCII.GetBytes(hashMe)); return UpgradedV1 + hexEncode(hash) + createNonce(32); }
private static string computeSecret(HttpDigestChallenge challenge, string username, string password) { if (challenge.Algorithm==null || string.Compare(challenge.Algorithm, "md5" ,StringComparison.OrdinalIgnoreCase)==0) { return username + ":" + challenge.Realm + ":" + password; } else if (string.Compare(challenge.Algorithm, "md5-sess" ,StringComparison.OrdinalIgnoreCase)==0) { return hashString(username + ":" + challenge.Realm + ":" + password, challenge.MD5provider) + ":" + challenge.Nonce + ":" + challenge.ClientNonce; } if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_digest_hash_algorithm_not_supported, challenge.Algorithm)); return null; }
internal static Authorization Authenticate(HttpDigestChallenge digestChallenge, NetworkCredential NC, string spn, ChannelBinding binding) { string userName = NC.InternalGetUserName(); if (ValidationHelper.IsBlankString(userName)) { return(null); } string password = NC.InternalGetPassword(); bool flag = IsUpgraded(digestChallenge.Nonce, binding); if (flag) { digestChallenge.ServiceName = spn; digestChallenge.ChannelBinding = hashChannelBinding(binding, digestChallenge.MD5provider); } if (digestChallenge.QopPresent) { if ((digestChallenge.ClientNonce == null) || digestChallenge.Stale) { if (flag) { digestChallenge.ClientNonce = createUpgradedNonce(digestChallenge); } else { digestChallenge.ClientNonce = createNonce(0x20); } digestChallenge.NonceCount = 1; } else { digestChallenge.NonceCount++; } } StringBuilder builder = new StringBuilder(); Charset charset = DetectCharset(userName); if (!digestChallenge.UTF8Charset && (charset == Charset.UTF8)) { return(null); } Charset charset2 = DetectCharset(password); if (!digestChallenge.UTF8Charset && (charset2 == Charset.UTF8)) { return(null); } if (digestChallenge.UTF8Charset) { builder.Append(pair("charset", "utf-8", false)); builder.Append(","); if (charset == Charset.UTF8) { userName = CharsetEncode(userName, Charset.UTF8); builder.Append(pair("username", userName, true)); builder.Append(","); } else { builder.Append(pair("username", CharsetEncode(userName, Charset.UTF8), true)); builder.Append(","); userName = CharsetEncode(userName, charset); } } else { userName = CharsetEncode(userName, charset); builder.Append(pair("username", userName, true)); builder.Append(","); } password = CharsetEncode(password, charset2); builder.Append(pair("realm", digestChallenge.Realm, true)); builder.Append(","); builder.Append(pair("nonce", digestChallenge.Nonce, true)); builder.Append(","); builder.Append(pair("uri", digestChallenge.Uri, true)); if (digestChallenge.QopPresent) { if (digestChallenge.Algorithm != null) { builder.Append(","); builder.Append(pair("algorithm", digestChallenge.Algorithm, true)); } builder.Append(","); builder.Append(pair("cnonce", digestChallenge.ClientNonce, true)); builder.Append(","); builder.Append(pair("nc", digestChallenge.NonceCount.ToString("x8", NumberFormatInfo.InvariantInfo), false)); builder.Append(","); builder.Append(pair("qop", "auth", true)); if (flag) { builder.Append(","); builder.Append(pair("hashed-dirs", "service-name,channel-binding", true)); builder.Append(","); builder.Append(pair("service-name", digestChallenge.ServiceName, true)); builder.Append(","); builder.Append(pair("channel-binding", digestChallenge.ChannelBinding, true)); } } string str3 = responseValue(digestChallenge, userName, password); if (str3 == null) { return(null); } builder.Append(","); builder.Append(pair("response", str3, true)); if (digestChallenge.Opaque != null) { builder.Append(","); builder.Append(pair("opaque", digestChallenge.Opaque, true)); } return(new Authorization("Digest " + builder.ToString(), false)); }
// // this method parses the challenge and breaks it into the // fundamental pieces that Digest defines and understands // public static HttpDigestChallenge Interpret(string challenge, int startingPoint, HttpWebRequest httpWebRequest) { HttpDigestChallenge hdc = new HttpDigestChallenge(); hdc.HostName = httpWebRequest.ChallengedUri.Host; hdc.Method = httpWebRequest.CurrentMethod; hdc.Uri = httpWebRequest.Address.AbsolutePath; hdc.ChallengedUri = httpWebRequest.ChallengedUri; // // define the part of the challenge we really care about // startingPoint += DigestClient.SignatureSize; bool valid; int start, offset, index; string name, value; // forst time parse looking for a charset="utf-8" directive // not too bad, IIS 6.0, by default, sends this as the first directive. // if the server does not send this we'll end up parsing twice. start = startingPoint; for (;;) { offset = start; index = AuthenticationManager.SplitNoQuotes(challenge, ref offset); if (offset < 0) { break; } name = challenge.Substring(start, offset - start); if (string.Compare(name, DA_charset, true, CultureInfo.InvariantCulture) == 0) { if (index < 0) { value = unquote(challenge.Substring(offset + 1)); } else { value = unquote(challenge.Substring(offset + 1, index - offset - 1)); } GlobalLog.Print("HttpDigest::Interpret() server provided a hint to use [" + value + "] encoding"); if (string.Compare(value, "utf-8", true, CultureInfo.InvariantCulture) == 0) { hdc.UTF8Charset = true; break; } } if (index < 0) { break; } start = ++index; } // this time go through the directives, parse them and call defineAttribute() start = startingPoint; for (;;) { offset = start; index = AuthenticationManager.SplitNoQuotes(challenge, ref offset); GlobalLog.Print("HttpDigest::Interpret() SplitNoQuotes() returning index:" + index.ToString() + " offset:" + offset.ToString()); if (offset < 0) { break; } name = challenge.Substring(start, offset - start); if (index < 0) { value = unquote(challenge.Substring(offset + 1)); } else { value = unquote(challenge.Substring(offset + 1, index - offset - 1)); } if (hdc.UTF8Charset) { bool isAscii = true; for (int i = 0; i < value.Length; i++) { if (value[i] > (char)0x7F) { isAscii = false; break; } } if (!isAscii) { GlobalLog.Print("HttpDigest::Interpret() UTF8 decoding required value:[" + value + "]"); byte[] bytes = new byte[value.Length]; for (int i = 0; i < value.Length; i++) { bytes[i] = (byte)value[i]; } value = Encoding.UTF8.GetString(bytes); GlobalLog.Print("HttpDigest::Interpret() UTF8 decoded value:[" + value + "]"); } else { GlobalLog.Print("HttpDigest::Interpret() no need for special encoding"); } } valid = hdc.defineAttribute(name, value); GlobalLog.Print("HttpDigest::Interpret() defineAttribute(" + name + ", " + value + ") returns " + valid.ToString()); if (index < 0 || !valid) { break; } start = ++index; } return(hdc); }
public Authorization Authenticate(string challenge, WebRequest webRequest, ICredentials credentials) { GlobalLog.Print("DigestClient::Authenticate(): " + challenge); #if XP_WDIGEST if (ComNetOS.IsPostWin2K) { return(XPDigestClient.Authenticate(challenge, webRequest, credentials)); } #endif // #if XP_WDIGEST GlobalLog.Assert(credentials != null, "DigestClient::Authenticate() credentials==null", ""); if (credentials == null || credentials is SystemNetworkCredential) { return(null); } HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; GlobalLog.Assert(httpWebRequest != null, "DigestClient::Authenticate() httpWebRequest==null", ""); if (httpWebRequest == null || httpWebRequest.ChallengedUri == null) { // // there has been no challenge: // 1) the request never went on the wire // 2) somebody other than us is calling into AuthenticationManager // return(null); } int index = AuthenticationManager.FindSubstringNotInQuotes(challenge.ToLower(CultureInfo.InvariantCulture), Signature); if (index < 0) { return(null); } string[] prefixes = null; string rootPath = httpWebRequest.ChallengedUri.Scheme + "://" + httpWebRequest.ChallengedUri.Host; HttpDigestChallenge digestChallenge = HttpDigest.Interpret(challenge, index, httpWebRequest); if (digestChallenge == null) { return(null); } if (digestChallenge.Domain == null) { challengeCache.Add(rootPath, digestChallenge); } else { prefixes = digestChallenge.Domain.Split(" ".ToCharArray()); for (int i = 0; i < prefixes.Length; i++) { challengeCache.Add(prefixes[i], digestChallenge); } } Authorization digestResponse = HttpDigest.Authenticate(digestChallenge, credentials); if (digestResponse != null) { if (prefixes == null) { digestResponse.ProtectionRealm = new string[1]; digestResponse.ProtectionRealm[0] = rootPath; } else { digestResponse.ProtectionRealm = prefixes; } } return(digestResponse); }
public bool Update(string challenge, WebRequest webRequest) { GlobalLog.Print("DigestClient::Update(): [" + challenge + "]"); #if XP_WDIGEST if (ComNetOS.IsPostWin2K) { return(XPDigestClient.Update(challenge, webRequest)); } #endif // #if XP_WDIGEST HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; GlobalLog.Assert(httpWebRequest != null, "DigestClient::Update() httpWebRequest==null", ""); GlobalLog.Assert(httpWebRequest.ChallengedUri != null, "DigestClient::Update() httpWebRequest.ChallengedUri==null", ""); // here's how we know if the handshake is complete when we get the response back, // (keeping in mind that we need to support stale credentials): // !40X - complete & success // 40X & stale=false - complete & failure // 40X & stale=true - !complete if (httpWebRequest.ResponseStatusCode != httpWebRequest.CurrentAuthenticationState.StatusCodeMatch) { GlobalLog.Print("DigestClient::Update(): no status code match. returning true"); return(true); } int index = challenge == null ? -1 : AuthenticationManager.FindSubstringNotInQuotes(challenge.ToLower(CultureInfo.InvariantCulture), Signature); if (index < 0) { GlobalLog.Print("DigestClient::Update(): no challenge. returning true"); return(true); } int blobBegin = index + SignatureSize; string incoming = null; // // there may be multiple challenges. If the next character after the // package name is not a comma then it is challenge data // if (challenge.Length > blobBegin && challenge[blobBegin] != ',') { ++blobBegin; } else { index = -1; } if (index >= 0 && challenge.Length > blobBegin) { incoming = challenge.Substring(blobBegin); } HttpDigestChallenge digestChallenge = HttpDigest.Interpret(challenge, index, httpWebRequest); if (digestChallenge == null) { GlobalLog.Print("DigestClient::Update(): not a valid digest challenge. returning true"); return(true); } GlobalLog.Print("DigestClient::Update(): returning digestChallenge.Stale:" + digestChallenge.Stale.ToString()); return(!digestChallenge.Stale); }
// // CONSIDER V.NEXT // creating a static hashtable for server nonces and keep track of nonce count // internal static Authorization Authenticate(HttpDigestChallenge digestChallenge, NetworkCredential NC, string spn, ChannelBinding binding) { string username = NC.InternalGetUserName(); if (ValidationHelper.IsBlankString(username)) { return null; } string password = NC.InternalGetPassword(); bool upgraded = IsUpgraded(digestChallenge.Nonce, binding); if (upgraded) { digestChallenge.ServiceName = spn; digestChallenge.ChannelBinding = hashChannelBinding(binding, digestChallenge.MD5provider); } if (digestChallenge.QopPresent) { if (digestChallenge.ClientNonce==null || digestChallenge.Stale) { GlobalLog.Print("HttpDigest::Authenticate() QopPresent:True, need new nonce. digestChallenge.ClientNonce:" + ValidationHelper.ToString(digestChallenge.ClientNonce) + " digestChallenge.Stale:" + digestChallenge.Stale.ToString()); if (upgraded) { digestChallenge.ClientNonce = createUpgradedNonce(digestChallenge); } else { digestChallenge.ClientNonce = createNonce(32); } digestChallenge.NonceCount = 1; } else { GlobalLog.Print("HttpDigest::Authenticate() QopPresent:True, reusing nonce. digestChallenge.NonceCount:" + digestChallenge.NonceCount.ToString()); digestChallenge.NonceCount++; } } StringBuilder authorization = new StringBuilder(); // // look at username & password, if it's not ASCII we need to attempt some // kind of encoding because we need to calculate the hash on byte[] // Charset usernameCharset = DetectCharset(username); if (!digestChallenge.UTF8Charset && usernameCharset==Charset.UTF8) { GlobalLog.Print("HttpDigest::Authenticate() can't authenticate with UNICODE username. failing auth."); return null; } Charset passwordCharset = DetectCharset(password); if (!digestChallenge.UTF8Charset && passwordCharset==Charset.UTF8) { GlobalLog.Print("HttpDigest::Authenticate() can't authenticate with UNICODE password. failing auth."); return null; } if (digestChallenge.UTF8Charset) { // on the wire always use UTF8 when the server supports it authorization.Append(pair(DA_charset, "utf-8", false)); authorization.Append(","); if (usernameCharset==Charset.UTF8) { username = CharsetEncode(username, Charset.UTF8); authorization.Append(pair(DA_username, username, true)); authorization.Append(","); } else { authorization.Append(pair(DA_username, CharsetEncode(username, Charset.UTF8), true)); authorization.Append(","); username = CharsetEncode(username, usernameCharset); } } else { // otherwise UTF8 is not required username = CharsetEncode(username, usernameCharset); authorization.Append(pair(DA_username, username, true)); authorization.Append(","); } password = CharsetEncode(password, passwordCharset); // no special encoding for the realm since we're just going to echo it back (encoding must have happened on the server). authorization.Append(pair(DA_realm, digestChallenge.Realm, true)); authorization.Append(","); authorization.Append(pair(DA_nonce, digestChallenge.Nonce, true)); authorization.Append(","); authorization.Append(pair(DA_uri, digestChallenge.Uri, true)); if (digestChallenge.QopPresent) { if (digestChallenge.Algorithm!=null) { // consider: should we default to "MD5" here? IE does authorization.Append(","); authorization.Append(pair(DA_algorithm, digestChallenge.Algorithm, true)); // IE sends quotes - IIS needs them } authorization.Append(","); authorization.Append(pair(DA_cnonce, digestChallenge.ClientNonce, true)); authorization.Append(","); authorization.Append(pair(DA_nc, digestChallenge.NonceCount.ToString("x8", NumberFormatInfo.InvariantInfo), false)); // RAID#47397 // send only the QualityOfProtection we're using // since we support only "auth" that's what we will send out authorization.Append(","); authorization.Append(pair(DA_qop, SupportedQuality, true)); // IE sends quotes - IIS needs them if (upgraded) { authorization.Append(","); authorization.Append(pair(DA_hasheddirs, HashedDirs, true)); authorization.Append(","); authorization.Append(pair(DA_servicename, digestChallenge.ServiceName, true)); authorization.Append(","); authorization.Append(pair(DA_channelbinding, digestChallenge.ChannelBinding, true)); } } // warning: this must be computed here string responseValue = HttpDigest.responseValue(digestChallenge, username, password); if (responseValue==null) { return null; } authorization.Append(","); authorization.Append(pair(DA_response, responseValue, true)); // IE sends quotes - IIS needs them if (digestChallenge.Opaque!=null) { authorization.Append(","); authorization.Append(pair(DA_opaque, digestChallenge.Opaque, true)); } GlobalLog.Print("HttpDigest::Authenticate() digestChallenge.Stale:" + digestChallenge.Stale.ToString()); // completion is decided in Update() Authorization finalAuthorization = new Authorization(DigestClient.AuthType + " " + authorization.ToString(), false); return finalAuthorization; }
internal static HttpDigestChallenge Interpret(string challenge, int startingPoint, HttpWebRequest httpWebRequest) { int num2; string str2; HttpDigestChallenge challenge2 = new HttpDigestChallenge(); challenge2.SetFromRequest(httpWebRequest); startingPoint = (startingPoint == -1) ? 0 : (startingPoint + DigestClient.SignatureSize); int startIndex = startingPoint; Label_001F: num2 = startIndex; int num3 = AuthenticationManager.SplitNoQuotes(challenge, ref num2); if (num2 >= 0) { if (string.Compare(challenge.Substring(startIndex, num2 - startIndex), "charset", StringComparison.OrdinalIgnoreCase) == 0) { if (num3 < 0) { str2 = unquote(challenge.Substring(num2 + 1)); } else { str2 = unquote(challenge.Substring(num2 + 1, (num3 - num2) - 1)); } if (string.Compare(str2, "utf-8", StringComparison.OrdinalIgnoreCase) == 0) { challenge2.UTF8Charset = true; goto Label_009E; } } if (num3 >= 0) { startIndex = ++num3; goto Label_001F; } } Label_009E: startIndex = startingPoint; Label_00A0: num2 = startIndex; num3 = AuthenticationManager.SplitNoQuotes(challenge, ref num2); if (num2 >= 0) { string name = challenge.Substring(startIndex, num2 - startIndex); if (num3 < 0) { str2 = unquote(challenge.Substring(num2 + 1)); } else { str2 = unquote(challenge.Substring(num2 + 1, (num3 - num2) - 1)); } if (challenge2.UTF8Charset) { bool flag2 = true; for (int i = 0; i < str2.Length; i++) { if (str2[i] > '\x007f') { flag2 = false; break; } } if (!flag2) { byte[] bytes = new byte[str2.Length]; for (int j = 0; j < str2.Length; j++) { bytes[j] = (byte)str2[j]; } str2 = Encoding.UTF8.GetString(bytes); } } bool flag = challenge2.defineAttribute(name, str2); if ((num3 >= 0) && flag) { startIndex = ++num3; goto Label_00A0; } } if (challenge2.Nonce != null) { return(challenge2); } if (Logging.On) { Logging.PrintError(Logging.Web, SR.GetString("net_log_digest_requires_nonce")); } return(null); }
private static string RefineDigestChallenge(string challenge, int index) { string str = null; int num4; if ((challenge == null) || (index >= challenge.Length)) { throw new ArgumentOutOfRangeException("challenge", challenge); } int startIndex = index + SignatureSize; if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { index = -1; } if ((index < 0) || (challenge.Length <= startIndex)) { throw new ArgumentOutOfRangeException("challenge", challenge); } str = challenge.Substring(startIndex); int num3 = 0; bool flag = true; HttpDigestChallenge challenge2 = new HttpDigestChallenge(); Label_0070: num4 = num3; index = AuthenticationManager.SplitNoQuotes(str, ref num4); if (num4 >= 0) { string str3; string name = str.Substring(num3, num4 - num3); if (index < 0) { str3 = HttpDigest.unquote(str.Substring(num4 + 1)); } else { str3 = HttpDigest.unquote(str.Substring(num4 + 1, (index - num4) - 1)); } flag = challenge2.defineAttribute(name, str3); if ((index >= 0) && flag) { num3 = ++index; goto Label_0070; } } if ((!flag || (num4 < 0)) && (num3 < str.Length)) { str = (num3 > 0) ? str.Substring(0, num3 - 1) : ""; } return str; }
// // this method computes the response-value according to the // rules described in RFC2831 section 2.1.2.1 // private static string responseValue(HttpDigestChallenge challenge, string username, string password) { string secretString = computeSecret(challenge, username, password); if (secretString == null) { return null; } // we assume auth here, since it's the only one we support, the check happened earlier string dataString = challenge.Method + ":" + challenge.Uri; if (dataString == null) { return null; } string secret = hashString(secretString, challenge.MD5provider); string hexMD2 = hashString(dataString, challenge.MD5provider); string data = challenge.Nonce + ":" + (challenge.QopPresent ? challenge.NonceCount.ToString("x8", NumberFormatInfo.InvariantInfo) + ":" + challenge.ClientNonce + ":" + SupportedQuality + ":" + // challenge.QualityOfProtection + ":" + hexMD2 : hexMD2); return hashString(secret + ":" + data, challenge.MD5provider); }
// // CONSIDER V.NEXT // creating a static hashtable for server nonces and keep track of nonce count // public static Authorization Authenticate(HttpDigestChallenge digestChallenge, ICredentials credentials) { NetworkCredential NC = credentials.GetCredential(digestChallenge.ChallengedUri, DigestClient.Signature); GlobalLog.Print("HttpDigest::Authenticate() GetCredential() returns:" + ValidationHelper.ToString(NC)); if (NC == null) { return(null); } string username = NC.UserName; if (ValidationHelper.IsBlankString(username)) { return(null); } string password = NC.Password; if (digestChallenge.QopPresent) { if (digestChallenge.ClientNonce == null || digestChallenge.Stale) { GlobalLog.Print("HttpDigest::Authenticate() QopPresent:True, need new nonce. digestChallenge.ClientNonce:" + ValidationHelper.ToString(digestChallenge.ClientNonce) + " digestChallenge.Stale:" + digestChallenge.Stale.ToString()); digestChallenge.ClientNonce = createNonce(32); digestChallenge.NonceCount = 1; } else { GlobalLog.Print("HttpDigest::Authenticate() QopPresent:True, reusing nonce. digestChallenge.NonceCount:" + digestChallenge.NonceCount.ToString()); digestChallenge.NonceCount++; } } StringBuilder authorization = new StringBuilder(); // // look at username & password, if it's not ASCII we need to attempt some // kind of encoding because we need to calculate the hash on byte[] // Charset usernameCharset = DetectCharset(username); if (!digestChallenge.UTF8Charset && usernameCharset == Charset.UTF8) { GlobalLog.Print("HttpDigest::Authenticate() can't authenticate with UNICODE username. failing auth."); return(null); } Charset passwordCharset = DetectCharset(password); if (!digestChallenge.UTF8Charset && passwordCharset == Charset.UTF8) { GlobalLog.Print("HttpDigest::Authenticate() can't authenticate with UNICODE password. failing auth."); return(null); } if (digestChallenge.UTF8Charset) { // on the wire always use UTF8 when the server supports it authorization.Append(pair(DA_charset, "utf-8", false)); authorization.Append(","); if (usernameCharset == Charset.UTF8) { username = CharsetEncode(username, Charset.UTF8); authorization.Append(pair(DA_username, username, true)); authorization.Append(","); } else { authorization.Append(pair(DA_username, CharsetEncode(username, Charset.UTF8), true)); authorization.Append(","); username = CharsetEncode(username, usernameCharset); } } else { // otherwise UTF8 is not required username = CharsetEncode(username, usernameCharset); authorization.Append(pair(DA_username, username, true)); authorization.Append(","); } password = CharsetEncode(password, passwordCharset); // no special encoding for the realm since we're just going to echo it back (encoding must have happened on the server). authorization.Append(pair(DA_realm, digestChallenge.Realm, true)); authorization.Append(","); authorization.Append(pair(DA_nonce, digestChallenge.Nonce, true)); authorization.Append(","); authorization.Append(pair(DA_uri, digestChallenge.Uri, true)); if (digestChallenge.QopPresent) { // // RAID#47397 // send only the QualityOfProtection we're using // since we support only "auth" that's what we will send out // if (digestChallenge.Algorithm != null) { // // consider: should we default to "MD5" here? IE does // authorization.Append(","); authorization.Append(pair(DA_algorithm, digestChallenge.Algorithm, true)); // IE sends quotes - IIS needs them } authorization.Append(","); authorization.Append(pair(DA_cnonce, digestChallenge.ClientNonce, true)); authorization.Append(","); authorization.Append(pair(DA_nc, digestChallenge.NonceCount.ToString("x8"), false)); authorization.Append(","); authorization.Append(pair(DA_qop, SupportedQuality, true)); // IE sends quotes - IIS needs them } // warning: this must be computed here string responseValue = HttpDigest.responseValue(digestChallenge, username, password); if (responseValue == null) { return(null); } authorization.Append(","); authorization.Append(pair(DA_response, responseValue, true)); // IE sends quotes - IIS needs them if (digestChallenge.Opaque != null) { authorization.Append(","); authorization.Append(pair(DA_opaque, digestChallenge.Opaque, true)); } GlobalLog.Print("HttpDigest::Authenticate() digestChallenge.Stale:" + digestChallenge.Stale.ToString()); // completion is decided in Update() Authorization finalAuthorization = new Authorization(DigestClient.AuthType + " " + authorization.ToString(), false); return(finalAuthorization); }
private Authorization XPDoAuthenticate(string challenge, HttpWebRequest httpWebRequest, ICredentials credentials, bool preAuthenticate) { NTAuthentication securityContext = null; string incomingBlob = null; SecurityStatus status; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return(null); } securityContext = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this); incomingBlob = RefineDigestChallenge(challenge, index); } else { HttpDigestChallenge challenge2 = challengeCache.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri) as HttpDigestChallenge; if (challenge2 == null) { return(null); } challenge2 = challenge2.CopyAndIncrementNonce(); challenge2.SetFromRequest(httpWebRequest); incomingBlob = challenge2.ToBlob(); } UriComponents uriParts = 0; if (httpWebRequest.CurrentMethod.ConnectRequest) { uriParts = UriComponents.HostAndPort; } else if (httpWebRequest.UsesProxySemantics) { uriParts = UriComponents.HttpRequestUrl; } else { uriParts = UriComponents.PathAndQuery; } string parts = httpWebRequest.GetRemoteResourceUri().GetParts(uriParts, UriFormat.UriEscaped); if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(httpWebRequest.ChallengedUri, Signature); if ((credential == null) || (!(credential is SystemNetworkCredential) && (credential.InternalGetUserName().Length == 0))) { return(null); } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(httpWebRequest.ChallengedUri, httpWebRequest, credential, this)) { return(null); } string computeSpn = httpWebRequest.CurrentAuthenticationState.GetComputeSpn(httpWebRequest); ChannelBinding channelBinding = null; if (httpWebRequest.CurrentAuthenticationState.TransportContext != null) { channelBinding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("WDigest", credential, computeSpn, httpWebRequest, channelBinding); httpWebRequest.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string str4 = securityContext.GetOutgoingDigestBlob(incomingBlob, httpWebRequest.CurrentMethod.Name, parts, null, false, false, out status); if (str4 == null) { return(null); } Authorization authorization = new Authorization("Digest " + str4, securityContext.IsCompleted, string.Empty, securityContext.IsMutualAuthFlag); if (!preAuthenticate && httpWebRequest.PreAuthenticate) { HttpDigestChallenge challenge3 = HttpDigest.Interpret(incomingBlob, -1, httpWebRequest); string[] strArray = (challenge3.Domain == null) ? new string[] { httpWebRequest.ChallengedUri.GetParts(UriComponents.SchemeAndServer, UriFormat.UriEscaped) } : challenge3.Domain.Split(singleSpaceArray); authorization.ProtectionRealm = (challenge3.Domain == null) ? null : strArray; for (int i = 0; i < strArray.Length; i++) { challengeCache.Add(strArray[i], challenge3); } } return(authorization); }