Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: net/http/http_auth_controller.cc

Issue 2866018: Revert 50647 - Create HttpAuthController.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/http/http_auth_controller.h ('k') | net/http/http_network_transaction.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/http/http_auth_controller.h"
6
7 #include "base/string_util.h"
8 #include "net/base/host_resolver.h"
9 #include "net/base/net_util.h"
10 #include "net/http/http_auth_handler_factory.h"
11 #include "net/http/http_network_session.h"
12 #include "net/http/http_request_headers.h"
13 #include "net/http/http_request_info.h"
14
15 namespace net {
16
17 namespace {
18
19 // Returns a log message for all the response headers related to the auth
20 // challenge.
21 std::string AuthChallengeLogMessage(HttpResponseHeaders* headers) {
22 std::string msg;
23 std::string header_val;
24 void* iter = NULL;
25 while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
26 msg.append("\n Has header Proxy-Authenticate: ");
27 msg.append(header_val);
28 }
29
30 iter = NULL;
31 while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
32 msg.append("\n Has header WWW-Authenticate: ");
33 msg.append(header_val);
34 }
35
36 // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate
37 // authentication with a "Proxy-Support: Session-Based-Authentication"
38 // response header.
39 iter = NULL;
40 while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
41 msg.append("\n Has header Proxy-Support: ");
42 msg.append(header_val);
43 }
44
45 return msg;
46 }
47
48 } // namespace
49
50 HttpAuthController::HttpAuthController(
51 HttpAuth::Target target,
52 const GURL& auth_url,
53 scoped_refptr<HttpNetworkSession> session,
54 const BoundNetLog& net_log)
55 : target_(target),
56 auth_url_(auth_url),
57 auth_origin_(auth_url.GetOrigin()),
58 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()),
59 embedded_identity_used_(false),
60 default_credentials_used_(false),
61 session_(session),
62 net_log_(net_log) {
63 }
64
65 int HttpAuthController::MaybeGenerateAuthToken(const HttpRequestInfo* request,
66 CompletionCallback* callback) {
67 bool needs_auth = HaveAuth() || SelectPreemptiveAuth();
68 if (!needs_auth)
69 return OK;
70 const std::wstring* username = NULL;
71 const std::wstring* password = NULL;
72 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) {
73 username = &identity_.username;
74 password = &identity_.password;
75 }
76 DCHECK(auth_token_.empty());
77 return handler_->GenerateAuthToken(username, password, request, callback,
78 &auth_token_);
79 }
80
81 bool HttpAuthController::SelectPreemptiveAuth() {
82 DCHECK(!HaveAuth());
83 DCHECK(identity_.invalid);
84
85 // Don't do preemptive authorization if the URL contains a username/password,
86 // since we must first be challenged in order to use the URL's identity.
87 if (auth_url_.has_username())
88 return false;
89
90 // SelectPreemptiveAuth() is on the critical path for each request, so it
91 // is expected to be fast. LookupByPath() is fast in the common case, since
92 // the number of http auth cache entries is expected to be very small.
93 // (For most users in fact, it will be 0.)
94 HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByPath(
95 auth_origin_, auth_path_);
96 if (!entry)
97 return false;
98
99 // Try to create a handler using the previous auth challenge.
100 scoped_ptr<HttpAuthHandler> handler_preemptive;
101 int rv_create = session_->http_auth_handler_factory()->
102 CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_,
103 auth_origin_,
104 entry->IncrementNonceCount(),
105 net_log_, &handler_preemptive);
106 if (rv_create != OK)
107 return false;
108
109 // Set the state
110 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
111 identity_.invalid = false;
112 identity_.username = entry->username();
113 identity_.password = entry->password();
114 handler_.swap(handler_preemptive);
115 return true;
116 }
117
118 void HttpAuthController::AddAuthorizationHeader(
119 HttpRequestHeaders* authorization_headers) {
120 DCHECK(HaveAuth());
121 DCHECK(!auth_token_.empty());
122 authorization_headers->SetHeader(
123 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
124 auth_token_.clear();
125 }
126
127 int HttpAuthController::HandleAuthChallenge(
128 scoped_refptr<HttpResponseHeaders> headers,
129 int load_flags,
130 bool establishing_tunnel) {
131 DCHECK(headers);
132 DCHECK(auth_origin_.is_valid());
133
134 LOG(INFO) << "The " << HttpAuth::GetAuthTargetString(target_) << " "
135 << auth_origin_ << " requested auth"
136 << AuthChallengeLogMessage(headers.get());
137
138 // The auth we tried just failed, hence it can't be valid. Remove it from
139 // the cache so it won't be used again.
140 // TODO(wtc): IsFinalRound is not the right condition. In a multi-round
141 // auth sequence, the server may fail the auth in round 1 if our first
142 // authorization header is broken. We should inspect response_.headers to
143 // determine if the server already failed the auth or wants us to continue.
144 // See http://crbug.com/21015.
145 if (HaveAuth() && handler_->IsFinalRound()) {
146 InvalidateRejectedAuthFromCache();
147 handler_.reset();
148 identity_ = HttpAuth::Identity();
149 }
150
151 identity_.invalid = true;
152
153 if (target_ != HttpAuth::AUTH_SERVER ||
154 !(load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) {
155 // Find the best authentication challenge that we support.
156 HttpAuth::ChooseBestChallenge(session_->http_auth_handler_factory(),
157 headers, target_, auth_origin_, net_log_,
158 &handler_);
159 }
160
161 if (!handler_.get()) {
162 if (establishing_tunnel) {
163 LOG(ERROR) << "Can't perform auth to the "
164 << HttpAuth::GetAuthTargetString(target_) << " "
165 << auth_origin_ << " when establishing a tunnel"
166 << AuthChallengeLogMessage(headers.get());
167
168 // We are establishing a tunnel, we can't show the error page because an
169 // active network attacker could control its contents. Instead, we just
170 // fail to establish the tunnel.
171 DCHECK(target_ == HttpAuth::AUTH_PROXY);
172 return ERR_PROXY_AUTH_REQUESTED;
173 }
174 // We found no supported challenge -- let the transaction continue
175 // so we end up displaying the error page.
176 return OK;
177 }
178
179 if (handler_->NeedsIdentity()) {
180 // Pick a new auth identity to try, by looking to the URL and auth cache.
181 // If an identity to try is found, it is saved to identity_.
182 SelectNextAuthIdentityToTry();
183 } else {
184 // Proceed with the existing identity or a null identity.
185 //
186 // TODO(wtc): Add a safeguard against infinite transaction restarts, if
187 // the server keeps returning "NTLM".
188 identity_.invalid = false;
189 }
190
191 // From this point on, we are restartable.
192
193 if (identity_.invalid) {
194 // We have exhausted all identity possibilities, all we can do now is
195 // pass the challenge information back to the client.
196 PopulateAuthChallenge();
197 }
198
199 // SPN determination (for Negotiate) requires a DNS lookup to find the
200 // canonical name. This needs to be done asynchronously to prevent blocking
201 // the IO thread.
202 if (handler_->NeedsCanonicalName())
203 return ERR_AUTH_NEEDS_CANONICAL_NAME;
204
205 return OK;
206 }
207
208 int HttpAuthController::ResolveCanonicalName(CompletionCallback* callback) {
209 DCHECK(handler_.get());
210 return handler_->ResolveCanonicalName(session_->host_resolver(), callback);
211 }
212
213 void HttpAuthController::ResetAuth(const std::wstring& username,
214 const std::wstring& password) {
215 DCHECK(identity_.invalid || (username.empty() && password.empty()));
216
217 if (identity_.invalid) {
218 // Update the username/password.
219 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
220 identity_.invalid = false;
221 identity_.username = username;
222 identity_.password = password;
223 }
224
225 DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
226
227 // Add the auth entry to the cache before restarting. We don't know whether
228 // the identity is valid yet, but if it is valid we want other transactions
229 // to know about it. If an entry for (origin, handler->realm()) already
230 // exists, we update it.
231 //
232 // If identity_.source is HttpAuth::IDENT_SRC_NONE or
233 // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no
234 // identity because identity is not required yet or we're using default
235 // credentials.
236 //
237 // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
238 // round 1 and round 2, which is redundant but correct. It would be nice
239 // to add an auth entry to the cache only once, preferrably in round 1.
240 // See http://crbug.com/21015.
241 switch (identity_.source) {
242 case HttpAuth::IDENT_SRC_NONE:
243 case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
244 break;
245 default:
246 session_->auth_cache()->Add(auth_origin_, handler_->realm(),
247 handler_->scheme(), handler_->challenge(),
248 identity_.username, identity_.password,
249 auth_path_);
250 break;
251 }
252 }
253
254 void HttpAuthController::InvalidateRejectedAuthFromCache() {
255 DCHECK(HaveAuth());
256
257 // TODO(eroman): this short-circuit can be relaxed. If the realm of
258 // the preemptively used auth entry matches the realm of the subsequent
259 // challenge, then we can invalidate the preemptively used entry.
260 // Otherwise as-is we may send the failed credentials one extra time.
261 if (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP)
262 return;
263
264 // Clear the cache entry for the identity we just failed on.
265 // Note: we require the username/password to match before invalidating
266 // since the entry in the cache may be newer than what we used last time.
267 session_->auth_cache()->Remove(auth_origin_, handler_->realm(),
268 handler_->scheme(), identity_.username,
269 identity_.password);
270 }
271
272 bool HttpAuthController::SelectNextAuthIdentityToTry() {
273 DCHECK(handler_.get());
274 DCHECK(identity_.invalid);
275
276 // Try to use the username/password encoded into the URL first.
277 if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
278 !embedded_identity_used_) {
279 identity_.source = HttpAuth::IDENT_SRC_URL;
280 identity_.invalid = false;
281 // Extract the username:password from the URL.
282 GetIdentityFromURL(auth_url_,
283 &identity_.username,
284 &identity_.password);
285 embedded_identity_used_ = true;
286 // TODO(eroman): If the password is blank, should we also try combining
287 // with a password from the cache?
288 return true;
289 }
290
291 // Check the auth cache for a realm entry.
292 HttpAuthCache::Entry* entry =
293 session_->auth_cache()->Lookup(auth_origin_, handler_->realm(),
294 handler_->scheme());
295
296 if (entry) {
297 identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
298 identity_.invalid = false;
299 identity_.username = entry->username();
300 identity_.password = entry->password();
301 return true;
302 }
303
304 // Use default credentials (single sign on) if this is the first attempt
305 // at identity. Do not allow multiple times as it will infinite loop.
306 // We use default credentials after checking the auth cache so that if
307 // single sign-on doesn't work, we won't try default credentials for future
308 // transactions.
309 if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
310 identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
311 identity_.invalid = false;
312 default_credentials_used_ = true;
313 return true;
314 }
315
316 return false;
317 }
318
319 void HttpAuthController::PopulateAuthChallenge() {
320 // Populates response_.auth_challenge with the authentication challenge info.
321 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
322
323 auth_info_ = new AuthChallengeInfo;
324 auth_info_->is_proxy = target_ == HttpAuth::AUTH_PROXY;
325 auth_info_->host_and_port = ASCIIToWide(GetHostAndPort(auth_origin_));
326 auth_info_->scheme = ASCIIToWide(handler_->scheme());
327 // TODO(eroman): decode realm according to RFC 2047.
328 auth_info_->realm = ASCIIToWide(handler_->realm());
329 }
330
331 } // namespace net
OLDNEW
« no previous file with comments | « net/http/http_auth_controller.h ('k') | net/http/http_network_transaction.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698