| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/http/http_auth_handler_digest.h" | 5 #include "net/http/http_auth_handler_digest.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/md5.h" | 10 #include "base/md5.h" |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 const std::string& method, | 166 const std::string& method, |
| 167 const std::string& path, | 167 const std::string& path, |
| 168 const string16& username, | 168 const string16& username, |
| 169 const string16& password, | 169 const string16& password, |
| 170 const std::string& cnonce, | 170 const std::string& cnonce, |
| 171 int nonce_count) const { | 171 int nonce_count) const { |
| 172 // the nonce-count is an 8 digit hex string. | 172 // the nonce-count is an 8 digit hex string. |
| 173 std::string nc = StringPrintf("%08x", nonce_count); | 173 std::string nc = StringPrintf("%08x", nonce_count); |
| 174 | 174 |
| 175 // TODO(eroman): is this the right encoding? | 175 // TODO(eroman): is this the right encoding? |
| 176 std::string authorization = std::string("Digest username=") + | 176 std::string authorization = (std::string("Digest username=") + |
| 177 HttpUtil::Quote(UTF16ToUTF8(username)); | 177 HttpUtil::Quote(UTF16ToUTF8(username))); |
| 178 authorization += ", realm=" + HttpUtil::Quote(realm_); | 178 authorization += ", realm=" + HttpUtil::Quote(realm_); |
| 179 authorization += ", nonce=" + HttpUtil::Quote(nonce_); | 179 authorization += ", nonce=" + HttpUtil::Quote(nonce_); |
| 180 authorization += ", uri=" + HttpUtil::Quote(path); | 180 authorization += ", uri=" + HttpUtil::Quote(path); |
| 181 | 181 |
| 182 if (algorithm_ != ALGORITHM_UNSPECIFIED) { | 182 if (algorithm_ != ALGORITHM_UNSPECIFIED) { |
| 183 authorization += ", algorithm=" + AlgorithmToString(algorithm_); | 183 authorization += ", algorithm=" + AlgorithmToString(algorithm_); |
| 184 } | 184 } |
| 185 std::string response = AssembleResponseDigest(method, path, username, | 185 std::string response = AssembleResponseDigest(method, path, username, |
| 186 password, cnonce, nc); | 186 password, cnonce, nc); |
| 187 // No need to call HttpUtil::Quote() as the response digest cannot contain | 187 // No need to call HttpUtil::Quote() as the response digest cannot contain |
| 188 // any characters needing to be escaped. | 188 // any characters needing to be escaped. |
| 189 authorization += ", response=\"" + response + "\""; | 189 authorization += ", response=\"" + response + "\""; |
| 190 | 190 |
| 191 if (!opaque_.empty()) { | 191 if (!opaque_.empty()) { |
| 192 authorization += ", opaque=" + HttpUtil::Quote(opaque_); | 192 authorization += ", opaque=" + HttpUtil::Quote(opaque_); |
| 193 } | 193 } |
| 194 if (qop_ != QOP_UNSPECIFIED) { | 194 if (qop_ != QOP_UNSPECIFIED) { |
| 195 // TODO(eroman): Supposedly IIS server requires quotes surrounding qop. | 195 // TODO(eroman): Supposedly IIS server requires quotes surrounding qop. |
| 196 authorization += ", qop=" + QopToString(qop_); | 196 authorization += ", qop=" + QopToString(qop_); |
| 197 authorization += ", nc=" + nc; | 197 authorization += ", nc=" + nc; |
| 198 authorization += ", cnonce=" + HttpUtil::Quote(cnonce); | 198 authorization += ", cnonce=" + HttpUtil::Quote(cnonce); |
| 199 } | 199 } |
| 200 | 200 |
| 201 return authorization; | 201 return authorization; |
| 202 } | 202 } |
| 203 | 203 |
| 204 bool HttpAuthHandlerDigest::Init(HttpAuth::ChallengeTokenizer* challenge) { |
| 205 return ParseChallenge(challenge); |
| 206 } |
| 207 |
| 208 HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( |
| 209 HttpAuth::ChallengeTokenizer* challenge) { |
| 210 // Even though Digest is not connection based, a "second round" is parsed |
| 211 // to differentiate between stale and rejected responses. |
| 212 // Note that the state of the current handler is not mutated - this way if |
| 213 // there is a rejection the realm hasn't changed. |
| 214 if (!challenge->valid() || |
| 215 !LowerCaseEqualsASCII(challenge->scheme(), "digest")) |
| 216 return HttpAuth::AUTHORIZATION_RESULT_INVALID; |
| 217 |
| 218 // Try to find the "stale" value. |
| 219 while (challenge->GetNext()) { |
| 220 if (!LowerCaseEqualsASCII(challenge->name(), "stale")) |
| 221 continue; |
| 222 if (LowerCaseEqualsASCII(challenge->unquoted_value(), "true")) |
| 223 return HttpAuth::AUTHORIZATION_RESULT_STALE; |
| 224 } |
| 225 |
| 226 return HttpAuth::AUTHORIZATION_RESULT_REJECT; |
| 227 } |
| 228 |
| 204 // The digest challenge header looks like: | 229 // The digest challenge header looks like: |
| 205 // WWW-Authenticate: Digest | 230 // WWW-Authenticate: Digest |
| 206 // [realm="<realm-value>"] | 231 // [realm="<realm-value>"] |
| 207 // nonce="<nonce-value>" | 232 // nonce="<nonce-value>" |
| 208 // [domain="<list-of-URIs>"] | 233 // [domain="<list-of-URIs>"] |
| 209 // [opaque="<opaque-token-value>"] | 234 // [opaque="<opaque-token-value>"] |
| 210 // [stale="<true-or-false>"] | 235 // [stale="<true-or-false>"] |
| 211 // [algorithm="<digest-algorithm>"] | 236 // [algorithm="<digest-algorithm>"] |
| 212 // [qop="<list-of-qop-values>"] | 237 // [qop="<list-of-qop-values>"] |
| 213 // [<extension-directive>] | 238 // [<extension-directive>] |
| (...skipping 10 matching lines...) Expand all Loading... |
| 224 scheme_ = "digest"; | 249 scheme_ = "digest"; |
| 225 score_ = 2; | 250 score_ = 2; |
| 226 properties_ = ENCRYPTS_IDENTITY; | 251 properties_ = ENCRYPTS_IDENTITY; |
| 227 | 252 |
| 228 // Initialize to defaults. | 253 // Initialize to defaults. |
| 229 stale_ = false; | 254 stale_ = false; |
| 230 algorithm_ = ALGORITHM_UNSPECIFIED; | 255 algorithm_ = ALGORITHM_UNSPECIFIED; |
| 231 qop_ = QOP_UNSPECIFIED; | 256 qop_ = QOP_UNSPECIFIED; |
| 232 realm_ = nonce_ = domain_ = opaque_ = std::string(); | 257 realm_ = nonce_ = domain_ = opaque_ = std::string(); |
| 233 | 258 |
| 259 // FAIL -- Couldn't match auth-scheme. |
| 234 if (!challenge->valid() || | 260 if (!challenge->valid() || |
| 235 !LowerCaseEqualsASCII(challenge->scheme(), "digest")) | 261 !LowerCaseEqualsASCII(challenge->scheme(), "digest")) |
| 236 return false; // FAIL -- Couldn't match auth-scheme. | 262 return false; |
| 237 | 263 |
| 238 // Loop through all the properties. | 264 // Loop through all the properties. |
| 239 while (challenge->GetNext()) { | 265 while (challenge->GetNext()) { |
| 240 if (challenge->value().empty()) { | 266 if (challenge->value().empty()) { |
| 241 DLOG(INFO) << "Invalid digest property"; | 267 DLOG(INFO) << "Invalid digest property"; |
| 242 return false; | 268 return false; |
| 243 } | 269 } |
| 244 | 270 |
| 271 // FAIL -- couldn't parse a property. |
| 245 if (!ParseChallengeProperty(challenge->name(), challenge->unquoted_value())) | 272 if (!ParseChallengeProperty(challenge->name(), challenge->unquoted_value())) |
| 246 return false; // FAIL -- couldn't parse a property. | 273 return false; |
| 247 } | 274 } |
| 248 | 275 |
| 249 // Check if tokenizer failed. | 276 // Check if tokenizer failed. |
| 250 if (!challenge->valid()) | 277 if (!challenge->valid()) |
| 251 return false; // FAIL | 278 return false; |
| 252 | 279 |
| 253 // Check that a minimum set of properties were provided. | 280 // Check that a minimum set of properties were provided. |
| 254 if (nonce_.empty()) | 281 if (nonce_.empty()) |
| 255 return false; // FAIL | 282 return false; |
| 256 | 283 |
| 257 return true; | 284 return true; |
| 258 } | 285 } |
| 259 | 286 |
| 260 bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name, | 287 bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name, |
| 261 const std::string& value) { | 288 const std::string& value) { |
| 262 if (LowerCaseEqualsASCII(name, "realm")) { | 289 if (LowerCaseEqualsASCII(name, "realm")) { |
| 263 realm_ = value; | 290 realm_ = value; |
| 264 } else if (LowerCaseEqualsASCII(name, "nonce")) { | 291 } else if (LowerCaseEqualsASCII(name, "nonce")) { |
| 265 nonce_ = value; | 292 nonce_ = value; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 // method and only constructing when valid. | 342 // method and only constructing when valid. |
| 316 scoped_ptr<HttpAuthHandler> tmp_handler( | 343 scoped_ptr<HttpAuthHandler> tmp_handler( |
| 317 new HttpAuthHandlerDigest(digest_nonce_count)); | 344 new HttpAuthHandlerDigest(digest_nonce_count)); |
| 318 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) | 345 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
| 319 return ERR_INVALID_RESPONSE; | 346 return ERR_INVALID_RESPONSE; |
| 320 handler->swap(tmp_handler); | 347 handler->swap(tmp_handler); |
| 321 return OK; | 348 return OK; |
| 322 } | 349 } |
| 323 | 350 |
| 324 } // namespace net | 351 } // namespace net |
| OLD | NEW |