| 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> |
| 8 |
| 7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 8 #include "base/md5.h" | 10 #include "base/md5.h" |
| 9 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
| 10 #include "base/string_util.h" | 12 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 12 #include "net/base/net_errors.h" | 14 #include "net/base/net_errors.h" |
| 13 #include "net/base/net_util.h" | 15 #include "net/base/net_util.h" |
| 14 #include "net/http/http_auth.h" | 16 #include "net/http/http_auth.h" |
| 15 #include "net/http/http_request_info.h" | 17 #include "net/http/http_request_info.h" |
| 16 #include "net/http/http_util.h" | 18 #include "net/http/http_util.h" |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 case ALGORITHM_MD5: | 81 case ALGORITHM_MD5: |
| 80 return "MD5"; | 82 return "MD5"; |
| 81 case ALGORITHM_MD5_SESS: | 83 case ALGORITHM_MD5_SESS: |
| 82 return "MD5-sess"; | 84 return "MD5-sess"; |
| 83 default: | 85 default: |
| 84 return ""; | 86 return ""; |
| 85 } | 87 } |
| 86 } | 88 } |
| 87 | 89 |
| 88 int HttpAuthHandlerDigest::GenerateAuthTokenImpl( | 90 int HttpAuthHandlerDigest::GenerateAuthTokenImpl( |
| 89 const std::wstring* username, | 91 const string16* username, |
| 90 const std::wstring* password, | 92 const string16* password, |
| 91 const HttpRequestInfo* request, | 93 const HttpRequestInfo* request, |
| 92 CompletionCallback* callback, | 94 CompletionCallback* callback, |
| 93 std::string* auth_token) { | 95 std::string* auth_token) { |
| 94 // Generate a random client nonce. | 96 // Generate a random client nonce. |
| 95 std::string cnonce = GenerateNonce(); | 97 std::string cnonce = GenerateNonce(); |
| 96 | 98 |
| 97 // Extract the request method and path -- the meaning of 'path' is overloaded | 99 // Extract the request method and path -- the meaning of 'path' is overloaded |
| 98 // in certain cases, to be a hostname. | 100 // in certain cases, to be a hostname. |
| 99 std::string method; | 101 std::string method; |
| 100 std::string path; | 102 std::string path; |
| 101 GetRequestMethodAndPath(request, &method, &path); | 103 GetRequestMethodAndPath(request, &method, &path); |
| 102 | 104 |
| 103 *auth_token = AssembleCredentials(method, path, | 105 *auth_token = AssembleCredentials(method, path, |
| 104 // TODO(eroman): is this the right encoding? | 106 *username, |
| 105 WideToUTF8(*username), | 107 *password, |
| 106 WideToUTF8(*password), | |
| 107 cnonce, nonce_count_); | 108 cnonce, nonce_count_); |
| 108 return OK; | 109 return OK; |
| 109 } | 110 } |
| 110 | 111 |
| 111 void HttpAuthHandlerDigest::GetRequestMethodAndPath( | 112 void HttpAuthHandlerDigest::GetRequestMethodAndPath( |
| 112 const HttpRequestInfo* request, | 113 const HttpRequestInfo* request, |
| 113 std::string* method, | 114 std::string* method, |
| 114 std::string* path) const { | 115 std::string* path) const { |
| 115 DCHECK(request); | 116 DCHECK(request); |
| 116 | 117 |
| 117 const GURL& url = request->url; | 118 const GURL& url = request->url; |
| 118 | 119 |
| 119 if (target_ == HttpAuth::AUTH_PROXY && url.SchemeIs("https")) { | 120 if (target_ == HttpAuth::AUTH_PROXY && url.SchemeIs("https")) { |
| 120 *method = "CONNECT"; | 121 *method = "CONNECT"; |
| 121 *path = GetHostAndPort(url); | 122 *path = GetHostAndPort(url); |
| 122 } else { | 123 } else { |
| 123 *method = request->method; | 124 *method = request->method; |
| 124 *path = HttpUtil::PathForRequest(url); | 125 *path = HttpUtil::PathForRequest(url); |
| 125 } | 126 } |
| 126 } | 127 } |
| 127 | 128 |
| 128 std::string HttpAuthHandlerDigest::AssembleResponseDigest( | 129 std::string HttpAuthHandlerDigest::AssembleResponseDigest( |
| 129 const std::string& method, | 130 const std::string& method, |
| 130 const std::string& path, | 131 const std::string& path, |
| 131 const std::string& username, | 132 const string16& username, |
| 132 const std::string& password, | 133 const string16& password, |
| 133 const std::string& cnonce, | 134 const std::string& cnonce, |
| 134 const std::string& nc) const { | 135 const std::string& nc) const { |
| 135 // ha1 = MD5(A1) | 136 // ha1 = MD5(A1) |
| 136 std::string ha1 = MD5String(username + ":" + realm_ + ":" + password); | 137 // TODO(eroman): is this the right encoding? |
| 138 std::string ha1 = MD5String(UTF16ToUTF8(username) + ":" + realm_ + ":" + |
| 139 UTF16ToUTF8(password)); |
| 137 if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS) | 140 if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS) |
| 138 ha1 = MD5String(ha1 + ":" + nonce_ + ":" + cnonce); | 141 ha1 = MD5String(ha1 + ":" + nonce_ + ":" + cnonce); |
| 139 | 142 |
| 140 // ha2 = MD5(A2) | 143 // ha2 = MD5(A2) |
| 141 // TODO(eroman): need to add MD5(req-entity-body) for qop=auth-int. | 144 // TODO(eroman): need to add MD5(req-entity-body) for qop=auth-int. |
| 142 std::string ha2 = MD5String(method + ":" + path); | 145 std::string ha2 = MD5String(method + ":" + path); |
| 143 | 146 |
| 144 std::string nc_part; | 147 std::string nc_part; |
| 145 if (qop_ != HttpAuthHandlerDigest::QOP_UNSPECIFIED) { | 148 if (qop_ != HttpAuthHandlerDigest::QOP_UNSPECIFIED) { |
| 146 nc_part = nc + ":" + cnonce + ":" + QopToString(qop_) + ":"; | 149 nc_part = nc + ":" + cnonce + ":" + QopToString(qop_) + ":"; |
| 147 } | 150 } |
| 148 | 151 |
| 149 return MD5String(ha1 + ":" + nonce_ + ":" + nc_part + ha2); | 152 return MD5String(ha1 + ":" + nonce_ + ":" + nc_part + ha2); |
| 150 } | 153 } |
| 151 | 154 |
| 152 std::string HttpAuthHandlerDigest::AssembleCredentials( | 155 std::string HttpAuthHandlerDigest::AssembleCredentials( |
| 153 const std::string& method, | 156 const std::string& method, |
| 154 const std::string& path, | 157 const std::string& path, |
| 155 const std::string& username, | 158 const string16& username, |
| 156 const std::string& password, | 159 const string16& password, |
| 157 const std::string& cnonce, | 160 const std::string& cnonce, |
| 158 int nonce_count) const { | 161 int nonce_count) const { |
| 159 // the nonce-count is an 8 digit hex string. | 162 // the nonce-count is an 8 digit hex string. |
| 160 std::string nc = StringPrintf("%08x", nonce_count); | 163 std::string nc = StringPrintf("%08x", nonce_count); |
| 161 | 164 |
| 165 // TODO(eroman): is this the right encoding? |
| 162 std::string authorization = std::string("Digest username=") + | 166 std::string authorization = std::string("Digest username=") + |
| 163 HttpUtil::Quote(username); | 167 HttpUtil::Quote(UTF16ToUTF8(username)); |
| 164 authorization += ", realm=" + HttpUtil::Quote(realm_); | 168 authorization += ", realm=" + HttpUtil::Quote(realm_); |
| 165 authorization += ", nonce=" + HttpUtil::Quote(nonce_); | 169 authorization += ", nonce=" + HttpUtil::Quote(nonce_); |
| 166 authorization += ", uri=" + HttpUtil::Quote(path); | 170 authorization += ", uri=" + HttpUtil::Quote(path); |
| 167 | 171 |
| 168 if (algorithm_ != ALGORITHM_UNSPECIFIED) { | 172 if (algorithm_ != ALGORITHM_UNSPECIFIED) { |
| 169 authorization += ", algorithm=" + AlgorithmToString(algorithm_); | 173 authorization += ", algorithm=" + AlgorithmToString(algorithm_); |
| 170 } | 174 } |
| 171 std::string response = AssembleResponseDigest(method, path, username, | 175 std::string response = AssembleResponseDigest(method, path, username, |
| 172 password, cnonce, nc); | 176 password, cnonce, nc); |
| 173 // No need to call HttpUtil::Quote() as the response digest cannot contain | 177 // No need to call HttpUtil::Quote() as the response digest cannot contain |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 properties_ = ENCRYPTS_IDENTITY; | 216 properties_ = ENCRYPTS_IDENTITY; |
| 213 | 217 |
| 214 // Initialize to defaults. | 218 // Initialize to defaults. |
| 215 stale_ = false; | 219 stale_ = false; |
| 216 algorithm_ = ALGORITHM_UNSPECIFIED; | 220 algorithm_ = ALGORITHM_UNSPECIFIED; |
| 217 qop_ = QOP_UNSPECIFIED; | 221 qop_ = QOP_UNSPECIFIED; |
| 218 realm_ = nonce_ = domain_ = opaque_ = std::string(); | 222 realm_ = nonce_ = domain_ = opaque_ = std::string(); |
| 219 | 223 |
| 220 if (!challenge->valid() || | 224 if (!challenge->valid() || |
| 221 !LowerCaseEqualsASCII(challenge->scheme(), "digest")) | 225 !LowerCaseEqualsASCII(challenge->scheme(), "digest")) |
| 222 return false; // FAIL -- Couldn't match auth-scheme. | 226 return false; // FAIL -- Couldn't match auth-scheme. |
| 223 | 227 |
| 224 // Loop through all the properties. | 228 // Loop through all the properties. |
| 225 while (challenge->GetNext()) { | 229 while (challenge->GetNext()) { |
| 226 if (challenge->value().empty()) { | 230 if (challenge->value().empty()) { |
| 227 DLOG(INFO) << "Invalid digest property"; | 231 DLOG(INFO) << "Invalid digest property"; |
| 228 return false; | 232 return false; |
| 229 } | 233 } |
| 230 | 234 |
| 231 if (!ParseChallengeProperty(challenge->name(), challenge->unquoted_value())) | 235 if (!ParseChallengeProperty(challenge->name(), challenge->unquoted_value())) |
| 232 return false; // FAIL -- couldn't parse a property. | 236 return false; // FAIL -- couldn't parse a property. |
| 233 } | 237 } |
| 234 | 238 |
| 235 // Check if tokenizer failed. | 239 // Check if tokenizer failed. |
| 236 if (!challenge->valid()) | 240 if (!challenge->valid()) |
| 237 return false; // FAIL | 241 return false; // FAIL |
| 238 | 242 |
| 239 // Check that a minimum set of properties were provided. | 243 // Check that a minimum set of properties were provided. |
| 240 if (nonce_.empty()) | 244 if (nonce_.empty()) |
| 241 return false; // FAIL | 245 return false; // FAIL |
| 242 | 246 |
| 243 return true; | 247 return true; |
| 244 } | 248 } |
| 245 | 249 |
| 246 bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name, | 250 bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name, |
| 247 const std::string& value) { | 251 const std::string& value) { |
| 248 if (LowerCaseEqualsASCII(name, "realm")) { | 252 if (LowerCaseEqualsASCII(name, "realm")) { |
| 249 realm_ = value; | 253 realm_ = value; |
| 250 } else if (LowerCaseEqualsASCII(name, "nonce")) { | 254 } else if (LowerCaseEqualsASCII(name, "nonce")) { |
| 251 nonce_ = value; | 255 nonce_ = value; |
| 252 } else if (LowerCaseEqualsASCII(name, "domain")) { | 256 } else if (LowerCaseEqualsASCII(name, "domain")) { |
| 253 domain_ = value; | 257 domain_ = value; |
| 254 } else if (LowerCaseEqualsASCII(name, "opaque")) { | 258 } else if (LowerCaseEqualsASCII(name, "opaque")) { |
| 255 opaque_ = value; | 259 opaque_ = value; |
| 256 } else if (LowerCaseEqualsASCII(name, "stale")) { | 260 } else if (LowerCaseEqualsASCII(name, "stale")) { |
| 257 // Parse the stale boolean. | 261 // Parse the stale boolean. |
| 258 stale_ = LowerCaseEqualsASCII(value, "true"); | 262 stale_ = LowerCaseEqualsASCII(value, "true"); |
| 259 } else if (LowerCaseEqualsASCII(name, "algorithm")) { | 263 } else if (LowerCaseEqualsASCII(name, "algorithm")) { |
| 260 // Parse the algorithm. | 264 // Parse the algorithm. |
| 261 if (LowerCaseEqualsASCII(value, "md5")) { | 265 if (LowerCaseEqualsASCII(value, "md5")) { |
| 262 algorithm_ = ALGORITHM_MD5; | 266 algorithm_ = ALGORITHM_MD5; |
| 263 } else if (LowerCaseEqualsASCII(value, "md5-sess")) { | 267 } else if (LowerCaseEqualsASCII(value, "md5-sess")) { |
| 264 algorithm_ = ALGORITHM_MD5_SESS; | 268 algorithm_ = ALGORITHM_MD5_SESS; |
| 265 } else { | 269 } else { |
| 266 DLOG(INFO) << "Unknown value of algorithm"; | 270 DLOG(INFO) << "Unknown value of algorithm"; |
| 267 return false; // FAIL -- unsupported value of algorithm. | 271 return false; // FAIL -- unsupported value of algorithm. |
| 268 } | 272 } |
| 269 } else if (LowerCaseEqualsASCII(name, "qop")) { | 273 } else if (LowerCaseEqualsASCII(name, "qop")) { |
| 270 // Parse the comma separated list of qops. | 274 // Parse the comma separated list of qops. |
| 271 HttpUtil::ValuesIterator qop_values(value.begin(), value.end(), ','); | 275 HttpUtil::ValuesIterator qop_values(value.begin(), value.end(), ','); |
| 272 while (qop_values.GetNext()) { | 276 while (qop_values.GetNext()) { |
| 273 if (LowerCaseEqualsASCII(qop_values.value(), "auth")) { | 277 if (LowerCaseEqualsASCII(qop_values.value(), "auth")) { |
| 274 qop_ |= QOP_AUTH; | 278 qop_ |= QOP_AUTH; |
| 275 } else if (LowerCaseEqualsASCII(qop_values.value(), "auth-int")) { | 279 } else if (LowerCaseEqualsASCII(qop_values.value(), "auth-int")) { |
| 276 qop_ |= QOP_AUTH_INT; | 280 qop_ |= QOP_AUTH_INT; |
| 277 } | 281 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 301 // method and only constructing when valid. | 305 // method and only constructing when valid. |
| 302 scoped_ptr<HttpAuthHandler> tmp_handler( | 306 scoped_ptr<HttpAuthHandler> tmp_handler( |
| 303 new HttpAuthHandlerDigest(digest_nonce_count)); | 307 new HttpAuthHandlerDigest(digest_nonce_count)); |
| 304 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) | 308 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
| 305 return ERR_INVALID_RESPONSE; | 309 return ERR_INVALID_RESPONSE; |
| 306 handler->swap(tmp_handler); | 310 handler->swap(tmp_handler); |
| 307 return OK; | 311 return OK; |
| 308 } | 312 } |
| 309 | 313 |
| 310 } // namespace net | 314 } // namespace net |
| OLD | NEW |