summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorvandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-23 23:36:07 +0000
committervandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-23 23:36:07 +0000
commit7fdbbf8d47bf4c2f5d2e86e1ad68443af8cfa8ff (patch)
tree21d98440a37f086a000a4ca4a318172da4d825b1 /net
parentbb2d13e580c488173714e54b0f277936c33654cb (diff)
downloadchromium_src-7fdbbf8d47bf4c2f5d2e86e1ad68443af8cfa8ff.zip
chromium_src-7fdbbf8d47bf4c2f5d2e86e1ad68443af8cfa8ff.tar.gz
chromium_src-7fdbbf8d47bf4c2f5d2e86e1ad68443af8cfa8ff.tar.bz2
Revert 50647 - Create HttpAuthController.
'Memory tests' is not happy. This packages up the auth state into a single class to enable a HttpProxyClientSocket class (which is needed for SSLClientSocketPool). BUG=30357 TEST=existing unit tests Review URL: http://codereview.chromium.org/2808020 TBR=vandebo@chromium.org Review URL: http://codereview.chromium.org/2866018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50669 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/http/http_auth.cc6
-rw-r--r--net/http/http_auth.h4
-rw-r--r--net/http/http_auth_controller.cc331
-rw-r--r--net/http/http_auth_controller.h135
-rw-r--r--net/http/http_network_transaction.cc387
-rw-r--r--net/http/http_network_transaction.h80
-rw-r--r--net/net.gyp2
8 files changed, 409 insertions, 539 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 1b21084..517ee4da 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -156,9 +156,6 @@ NET_ERROR(SSL_DECOMPRESSION_FAILURE_ALERT, -125)
// from servers with buggy DEFLATE support.
NET_ERROR(SSL_BAD_RECORD_MAC_ALERT, -126)
-// The HTTP auth handler requires a DNS lookup to find the canonical name.
-NET_ERROR(AUTH_NEEDS_CANONICAL_NAME, -127)
-
// Certificate error codes
//
// The values of certificate error codes must be consecutive.
diff --git a/net/http/http_auth.cc b/net/http/http_auth.cc
index 75b55a3..09214e4 100644
--- a/net/http/http_auth.cc
+++ b/net/http/http_auth.cc
@@ -169,10 +169,4 @@ std::string HttpAuth::GetAuthorizationHeaderName(Target target) {
}
}
-// static
-std::string HttpAuth::GetAuthTargetString(
- HttpAuth::Target target) {
- return target == HttpAuth::AUTH_PROXY ? "proxy" : "server";
-}
-
} // namespace net
diff --git a/net/http/http_auth.h b/net/http/http_auth.h
index 09b6f369..bba633d 100644
--- a/net/http/http_auth.h
+++ b/net/http/http_auth.h
@@ -78,10 +78,6 @@ class HttpAuth {
// (either Authorization or Proxy-Authorization).
static std::string GetAuthorizationHeaderName(Target target);
- // Returns a string representation of a Target value that can be used in log
- // messages.
- static std::string GetAuthTargetString(Target target);
-
// Iterate through the challenge headers, and pick the best one that
// we support. Obtains the implementation class for handling the challenge,
// and passes it back in |*handler|. If the existing handler in |*handler|
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
deleted file mode 100644
index b85ae03..0000000
--- a/net/http/http_auth_controller.cc
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/http/http_auth_controller.h"
-
-#include "base/string_util.h"
-#include "net/base/host_resolver.h"
-#include "net/base/net_util.h"
-#include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_network_session.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_request_info.h"
-
-namespace net {
-
-namespace {
-
-// Returns a log message for all the response headers related to the auth
-// challenge.
-std::string AuthChallengeLogMessage(HttpResponseHeaders* headers) {
- std::string msg;
- std::string header_val;
- void* iter = NULL;
- while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
- msg.append("\n Has header Proxy-Authenticate: ");
- msg.append(header_val);
- }
-
- iter = NULL;
- while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
- msg.append("\n Has header WWW-Authenticate: ");
- msg.append(header_val);
- }
-
- // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate
- // authentication with a "Proxy-Support: Session-Based-Authentication"
- // response header.
- iter = NULL;
- while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
- msg.append("\n Has header Proxy-Support: ");
- msg.append(header_val);
- }
-
- return msg;
-}
-
-} // namespace
-
-HttpAuthController::HttpAuthController(
- HttpAuth::Target target,
- const GURL& auth_url,
- scoped_refptr<HttpNetworkSession> session,
- const BoundNetLog& net_log)
- : target_(target),
- auth_url_(auth_url),
- auth_origin_(auth_url.GetOrigin()),
- auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()),
- embedded_identity_used_(false),
- default_credentials_used_(false),
- session_(session),
- net_log_(net_log) {
-}
-
-int HttpAuthController::MaybeGenerateAuthToken(const HttpRequestInfo* request,
- CompletionCallback* callback) {
- bool needs_auth = HaveAuth() || SelectPreemptiveAuth();
- if (!needs_auth)
- return OK;
- const std::wstring* username = NULL;
- const std::wstring* password = NULL;
- if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) {
- username = &identity_.username;
- password = &identity_.password;
- }
- DCHECK(auth_token_.empty());
- return handler_->GenerateAuthToken(username, password, request, callback,
- &auth_token_);
-}
-
-bool HttpAuthController::SelectPreemptiveAuth() {
- DCHECK(!HaveAuth());
- DCHECK(identity_.invalid);
-
- // Don't do preemptive authorization if the URL contains a username/password,
- // since we must first be challenged in order to use the URL's identity.
- if (auth_url_.has_username())
- return false;
-
- // SelectPreemptiveAuth() is on the critical path for each request, so it
- // is expected to be fast. LookupByPath() is fast in the common case, since
- // the number of http auth cache entries is expected to be very small.
- // (For most users in fact, it will be 0.)
- HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByPath(
- auth_origin_, auth_path_);
- if (!entry)
- return false;
-
- // Try to create a handler using the previous auth challenge.
- scoped_ptr<HttpAuthHandler> handler_preemptive;
- int rv_create = session_->http_auth_handler_factory()->
- CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_,
- auth_origin_,
- entry->IncrementNonceCount(),
- net_log_, &handler_preemptive);
- if (rv_create != OK)
- return false;
-
- // Set the state
- identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
- identity_.invalid = false;
- identity_.username = entry->username();
- identity_.password = entry->password();
- handler_.swap(handler_preemptive);
- return true;
-}
-
-void HttpAuthController::AddAuthorizationHeader(
- HttpRequestHeaders* authorization_headers) {
- DCHECK(HaveAuth());
- DCHECK(!auth_token_.empty());
- authorization_headers->SetHeader(
- HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
- auth_token_.clear();
-}
-
-int HttpAuthController::HandleAuthChallenge(
- scoped_refptr<HttpResponseHeaders> headers,
- int load_flags,
- bool establishing_tunnel) {
- DCHECK(headers);
- DCHECK(auth_origin_.is_valid());
-
- LOG(INFO) << "The " << HttpAuth::GetAuthTargetString(target_) << " "
- << auth_origin_ << " requested auth"
- << AuthChallengeLogMessage(headers.get());
-
- // The auth we tried just failed, hence it can't be valid. Remove it from
- // the cache so it won't be used again.
- // TODO(wtc): IsFinalRound is not the right condition. In a multi-round
- // auth sequence, the server may fail the auth in round 1 if our first
- // authorization header is broken. We should inspect response_.headers to
- // determine if the server already failed the auth or wants us to continue.
- // See http://crbug.com/21015.
- if (HaveAuth() && handler_->IsFinalRound()) {
- InvalidateRejectedAuthFromCache();
- handler_.reset();
- identity_ = HttpAuth::Identity();
- }
-
- identity_.invalid = true;
-
- if (target_ != HttpAuth::AUTH_SERVER ||
- !(load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) {
- // Find the best authentication challenge that we support.
- HttpAuth::ChooseBestChallenge(session_->http_auth_handler_factory(),
- headers, target_, auth_origin_, net_log_,
- &handler_);
- }
-
- if (!handler_.get()) {
- if (establishing_tunnel) {
- LOG(ERROR) << "Can't perform auth to the "
- << HttpAuth::GetAuthTargetString(target_) << " "
- << auth_origin_ << " when establishing a tunnel"
- << AuthChallengeLogMessage(headers.get());
-
- // We are establishing a tunnel, we can't show the error page because an
- // active network attacker could control its contents. Instead, we just
- // fail to establish the tunnel.
- DCHECK(target_ == HttpAuth::AUTH_PROXY);
- return ERR_PROXY_AUTH_REQUESTED;
- }
- // We found no supported challenge -- let the transaction continue
- // so we end up displaying the error page.
- return OK;
- }
-
- if (handler_->NeedsIdentity()) {
- // Pick a new auth identity to try, by looking to the URL and auth cache.
- // If an identity to try is found, it is saved to identity_.
- SelectNextAuthIdentityToTry();
- } else {
- // Proceed with the existing identity or a null identity.
- //
- // TODO(wtc): Add a safeguard against infinite transaction restarts, if
- // the server keeps returning "NTLM".
- identity_.invalid = false;
- }
-
- // From this point on, we are restartable.
-
- if (identity_.invalid) {
- // We have exhausted all identity possibilities, all we can do now is
- // pass the challenge information back to the client.
- PopulateAuthChallenge();
- }
-
- // SPN determination (for Negotiate) requires a DNS lookup to find the
- // canonical name. This needs to be done asynchronously to prevent blocking
- // the IO thread.
- if (handler_->NeedsCanonicalName())
- return ERR_AUTH_NEEDS_CANONICAL_NAME;
-
- return OK;
-}
-
-int HttpAuthController::ResolveCanonicalName(CompletionCallback* callback) {
- DCHECK(handler_.get());
- return handler_->ResolveCanonicalName(session_->host_resolver(), callback);
-}
-
-void HttpAuthController::ResetAuth(const std::wstring& username,
- const std::wstring& password) {
- DCHECK(identity_.invalid || (username.empty() && password.empty()));
-
- if (identity_.invalid) {
- // Update the username/password.
- identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
- identity_.invalid = false;
- identity_.username = username;
- identity_.password = password;
- }
-
- DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
-
- // Add the auth entry to the cache before restarting. We don't know whether
- // the identity is valid yet, but if it is valid we want other transactions
- // to know about it. If an entry for (origin, handler->realm()) already
- // exists, we update it.
- //
- // If identity_.source is HttpAuth::IDENT_SRC_NONE or
- // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no
- // identity because identity is not required yet or we're using default
- // credentials.
- //
- // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
- // round 1 and round 2, which is redundant but correct. It would be nice
- // to add an auth entry to the cache only once, preferrably in round 1.
- // See http://crbug.com/21015.
- switch (identity_.source) {
- case HttpAuth::IDENT_SRC_NONE:
- case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
- break;
- default:
- session_->auth_cache()->Add(auth_origin_, handler_->realm(),
- handler_->scheme(), handler_->challenge(),
- identity_.username, identity_.password,
- auth_path_);
- break;
- }
-}
-
-void HttpAuthController::InvalidateRejectedAuthFromCache() {
- DCHECK(HaveAuth());
-
- // TODO(eroman): this short-circuit can be relaxed. If the realm of
- // the preemptively used auth entry matches the realm of the subsequent
- // challenge, then we can invalidate the preemptively used entry.
- // Otherwise as-is we may send the failed credentials one extra time.
- if (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP)
- return;
-
- // Clear the cache entry for the identity we just failed on.
- // Note: we require the username/password to match before invalidating
- // since the entry in the cache may be newer than what we used last time.
- session_->auth_cache()->Remove(auth_origin_, handler_->realm(),
- handler_->scheme(), identity_.username,
- identity_.password);
-}
-
-bool HttpAuthController::SelectNextAuthIdentityToTry() {
- DCHECK(handler_.get());
- DCHECK(identity_.invalid);
-
- // Try to use the username/password encoded into the URL first.
- if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
- !embedded_identity_used_) {
- identity_.source = HttpAuth::IDENT_SRC_URL;
- identity_.invalid = false;
- // Extract the username:password from the URL.
- GetIdentityFromURL(auth_url_,
- &identity_.username,
- &identity_.password);
- embedded_identity_used_ = true;
- // TODO(eroman): If the password is blank, should we also try combining
- // with a password from the cache?
- return true;
- }
-
- // Check the auth cache for a realm entry.
- HttpAuthCache::Entry* entry =
- session_->auth_cache()->Lookup(auth_origin_, handler_->realm(),
- handler_->scheme());
-
- if (entry) {
- identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
- identity_.invalid = false;
- identity_.username = entry->username();
- identity_.password = entry->password();
- return true;
- }
-
- // Use default credentials (single sign on) if this is the first attempt
- // at identity. Do not allow multiple times as it will infinite loop.
- // We use default credentials after checking the auth cache so that if
- // single sign-on doesn't work, we won't try default credentials for future
- // transactions.
- if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
- identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
- identity_.invalid = false;
- default_credentials_used_ = true;
- return true;
- }
-
- return false;
-}
-
-void HttpAuthController::PopulateAuthChallenge() {
- // Populates response_.auth_challenge with the authentication challenge info.
- // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
-
- auth_info_ = new AuthChallengeInfo;
- auth_info_->is_proxy = target_ == HttpAuth::AUTH_PROXY;
- auth_info_->host_and_port = ASCIIToWide(GetHostAndPort(auth_origin_));
- auth_info_->scheme = ASCIIToWide(handler_->scheme());
- // TODO(eroman): decode realm according to RFC 2047.
- auth_info_->realm = ASCIIToWide(handler_->realm());
-}
-
-} // namespace net
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
deleted file mode 100644
index 80bdb28..0000000
--- a/net/http/http_auth_controller.h
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_HTTP_HTTP_AUTH_CONTROLLER_H_
-#define NET_HTTP_HTTP_AUTH_CONTROLLER_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
-#include "googleurl/src/gurl.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_log.h"
-#include "net/http/http_auth.h"
-
-namespace net {
-
-class AuthChallengeInfo;
-class HostResolver;
-class HttpNetworkSession;
-class HttpRequestHeaders;
-struct HttpRequestInfo;
-
-class HttpAuthController {
- public:
- // The arguments are self explanatory except possibly for |auth_url|, which
- // should be both the auth target and auth path in a single url argument.
- HttpAuthController(HttpAuth::Target target, const GURL& auth_url,
- scoped_refptr<HttpNetworkSession> session,
- const BoundNetLog& net_log);
-
- // Generate an authentication token for |target| if necessary. The return
- // value is a net error code. |OK| will be returned both in the case that
- // a token is correctly generated synchronously, as well as when no tokens
- // were necessary.
- int MaybeGenerateAuthToken(const HttpRequestInfo* request,
- CompletionCallback* callback);
-
- // Adds either the proxy auth header, or the origin server auth header,
- // as specified by |target_|.
- void AddAuthorizationHeader(HttpRequestHeaders* authorization_headers);
-
- // Checks for and handles HTTP status code 401 or 407.
- // |HandleAuthChallenge()| returns OK on success,
- // ERR_AUTH_NEEDS_CANONICAL_NAME if the handler needs the canonical name
- // resolved, or a network error code. It may also populate |auth_info_|.
- int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
- int load_flags, bool establishing_tunnel);
-
- int ResolveCanonicalName(CompletionCallback* callback);
-
- // Store the supplied credentials and prepare to restart the auth.
- void ResetAuth(const std::wstring& username, const std::wstring& password);
-
- bool HaveAuthHandler() const {
- return handler_.get() != NULL;
- }
-
- bool HaveAuth() const {
- return handler_.get() && !identity_.invalid;
- }
-
- // The caller receives ownership of the return AuthChallengeInfo.
- AuthChallengeInfo* auth_info() {
- return auth_info_.release();
- }
-
- private:
- // Searches the auth cache for an entry that encompasses the request's path.
- // If such an entry is found, updates |identity_| and |handler_| with the
- // cache entry's data and returns true.
- bool SelectPreemptiveAuth();
-
- // Invalidates any auth cache entries after authentication has failed.
- // The identity that was rejected is |identity_|.
- void InvalidateRejectedAuthFromCache();
-
- // Sets |identity_| to the next identity that the transaction should try. It
- // chooses candidates by searching the auth cache and the URL for a
- // username:password. Returns true if an identity was found.
- bool SelectNextAuthIdentityToTry();
-
- // Populates auth_info_ with the challenge information, so that
- // URLRequestHttpJob can prompt for a username/password.
- void PopulateAuthChallenge();
-
- // Indicates if this handler is for Proxy auth or Server auth.
- HttpAuth::Target target_;
-
- // Holds the {scheme, host, path, port} for the authentication target.
- const GURL auth_url_;
-
- // Holds the {scheme, host, port} for the authentication target.
- const GURL auth_origin_;
-
- // The absolute path of the resource needing authentication.
- // For proxy authentication the path is empty.
- const std::string auth_path_;
-
- // |handler_| encapsulates the logic for the particular auth-scheme.
- // This includes the challenge's parameters. If NULL, then there is no
- // associated auth handler.
- scoped_ptr<HttpAuthHandler> handler_;
-
- // |identity_| holds the (username/password) that should be used by
- // the handler_ to generate credentials. This identity can come from
- // a number of places (url, cache, prompt).
- HttpAuth::Identity identity_;
-
- // |auth_token_| contains the opaque string to pass to the proxy or
- // server to authenticate the client.
- std::string auth_token_;
-
- // Contains information about the auth challenge.
- scoped_refptr<AuthChallengeInfo> auth_info_;
-
- // True if we've used the username/password embedded in the URL. This
- // makes sure we use the embedded identity only once for the transaction,
- // preventing an infinite auth restart loop.
- bool embedded_identity_used_;
-
- // True if default credentials have already been tried for this transaction
- // in response to an HTTP authentication challenge.
- bool default_credentials_used_;
-
- scoped_refptr<HttpNetworkSession> session_;
-
- BoundNetLog net_log_;
-};
-
-} // namespace net
-
-#endif // NET_HTTP_HTTP_AUTH_CONTROLLER_H_
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index fe5498e..3269f42 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -296,6 +296,8 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
alternate_protocol_mode_(
g_use_alternate_protocols ? kUnspecified :
kDoNotUseAlternateProtocol),
+ embedded_identity_used_(false),
+ default_credentials_used_(false),
read_buf_len_(0),
next_state_(STATE_NONE) {
session->ssl_config_service()->GetSSLConfig(&ssl_config_);
@@ -395,9 +397,19 @@ int HttpNetworkTransaction::RestartWithAuth(
NOTREACHED();
return ERR_UNEXPECTED;
}
+
pending_auth_target_ = HttpAuth::AUTH_NONE;
- auth_controllers_[target]->ResetAuth(username, password);
+ DCHECK(auth_identity_[target].invalid ||
+ (username.empty() && password.empty()));
+
+ if (auth_identity_[target].invalid) {
+ // Update the username/password.
+ auth_identity_[target].source = HttpAuth::IDENT_SRC_EXTERNAL;
+ auth_identity_[target].invalid = false;
+ auth_identity_[target].username = username;
+ auth_identity_[target].password = password;
+ }
PrepareForAuthRestart(target);
@@ -411,6 +423,38 @@ int HttpNetworkTransaction::RestartWithAuth(
void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
DCHECK(HaveAuth(target));
+ DCHECK(auth_identity_[target].source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
+
+ // Add the auth entry to the cache before restarting. We don't know whether
+ // the identity is valid yet, but if it is valid we want other transactions
+ // to know about it. If an entry for (origin, handler->realm()) already
+ // exists, we update it.
+ //
+ // If auth_identity_[target].source is HttpAuth::IDENT_SRC_NONE or
+ // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, auth_identity_[target] contains
+ // no identity because identity is not required yet or we're using default
+ // credentials.
+ //
+ // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
+ // round 1 and round 2, which is redundant but correct. It would be nice
+ // to add an auth entry to the cache only once, preferrably in round 1.
+ // See http://crbug.com/21015.
+ switch (auth_identity_[target].source) {
+ case HttpAuth::IDENT_SRC_NONE:
+ case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
+ break;
+ default:
+ session_->auth_cache()->Add(
+ AuthOrigin(target),
+ auth_handler_[target]->realm(),
+ auth_handler_[target]->scheme(),
+ auth_handler_[target]->challenge(),
+ auth_identity_[target].username,
+ auth_identity_[target].password,
+ AuthPath(target));
+ break;
+ }
+
bool keep_alive = false;
// Even if the server says the connection is keep-alive, we have to be
// able to find the end of each response in order to reuse the connection.
@@ -812,16 +856,6 @@ int HttpNetworkTransaction::DoInitConnection() {
DCHECK(!connection_->is_initialized());
DCHECK(proxy_info_.proxy_server().is_valid());
- // Now that the proxy server has been resolved, create the auth_controllers_.
- for (int i = 0; i < HttpAuth::AUTH_NUM_TARGETS; i++) {
- HttpAuth::Target target = static_cast<HttpAuth::Target>(i);
- if (!auth_controllers_[target].get())
- auth_controllers_[target].reset(new HttpAuthController(target,
- AuthURL(target),
- session_,
- net_log_));
- }
-
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
using_ssl_ = request_->url.SchemeIs("https") ||
@@ -969,8 +1003,7 @@ void HttpNetworkTransaction::ClearTunnelState() {
int HttpNetworkTransaction::DoTunnelGenerateAuthToken() {
next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE;
- return auth_controllers_[HttpAuth::AUTH_PROXY]->MaybeGenerateAuthToken(
- request_, &io_callback_);
+ return MaybeGenerateAuthToken(HttpAuth::AUTH_PROXY);
}
int HttpNetworkTransaction::DoTunnelGenerateAuthTokenComplete(int rv) {
@@ -988,8 +1021,7 @@ int HttpNetworkTransaction::DoTunnelSendRequest() {
if (request_headers_.empty()) {
HttpRequestHeaders authorization_headers;
if (HaveAuth(HttpAuth::AUTH_PROXY))
- auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader(
- &authorization_headers);
+ AddAuthorizationHeader(HttpAuth::AUTH_PROXY, &authorization_headers);
std::string request_line;
HttpRequestHeaders request_headers;
BuildTunnelRequest(request_, authorization_headers, endpoint_,
@@ -1181,8 +1213,7 @@ int HttpNetworkTransaction::DoGenerateProxyAuthToken() {
next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE;
if (!ShouldApplyProxyAuth())
return OK;
- return auth_controllers_[HttpAuth::AUTH_PROXY]->MaybeGenerateAuthToken(
- request_, &io_callback_);
+ return MaybeGenerateAuthToken(HttpAuth::AUTH_PROXY);
}
int HttpNetworkTransaction::DoGenerateProxyAuthTokenComplete(int rv) {
@@ -1196,8 +1227,7 @@ int HttpNetworkTransaction::DoGenerateServerAuthToken() {
next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE;
if (!ShouldApplyServerAuth())
return OK;
- return auth_controllers_[HttpAuth::AUTH_SERVER]->MaybeGenerateAuthToken(
- request_, &io_callback_);
+ return MaybeGenerateAuthToken(HttpAuth::AUTH_SERVER);
}
int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) {
@@ -1229,11 +1259,9 @@ int HttpNetworkTransaction::DoSendRequest() {
bool have_server_auth = (ShouldApplyServerAuth() &&
HaveAuth(HttpAuth::AUTH_SERVER));
if (have_proxy_auth)
- auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader(
- &authorization_headers);
+ AddAuthorizationHeader(HttpAuth::AUTH_PROXY, &authorization_headers);
if (have_server_auth)
- auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader(
- &authorization_headers);
+ AddAuthorizationHeader(HttpAuth::AUTH_SERVER, &authorization_headers);
std::string request_line;
HttpRequestHeaders request_headers;
BuildRequestHeaders(request_, authorization_headers, request_body,
@@ -1381,10 +1409,10 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
}
int HttpNetworkTransaction::DoResolveCanonicalName() {
- DCHECK(auth_controllers_[pending_auth_target_].get());
+ DCHECK(auth_handler_[pending_auth_target_].get());
next_state_ = STATE_RESOLVE_CANONICAL_NAME_COMPLETE;
- return auth_controllers_[pending_auth_target_]->ResolveCanonicalName(
- &io_callback_);
+ return auth_handler_[pending_auth_target_]->
+ ResolveCanonicalName(session_->host_resolver(), &io_callback_);
}
int HttpNetworkTransaction::DoResolveCanonicalNameComplete(int result) {
@@ -1943,6 +1971,203 @@ bool HttpNetworkTransaction::ShouldApplyServerAuth() const {
return !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA);
}
+int HttpNetworkTransaction::MaybeGenerateAuthToken(HttpAuth::Target target) {
+ bool needs_auth = HaveAuth(target) || SelectPreemptiveAuth(target);
+ if (!needs_auth)
+ return OK;
+ const std::wstring* username = NULL;
+ const std::wstring* password = NULL;
+ const HttpAuth::Identity& identity = auth_identity_[target];
+ if (identity.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) {
+ username = &identity.username;
+ password = &identity.password;
+ }
+ DCHECK(auth_token_[target].empty());
+ return auth_handler_[target]->GenerateAuthToken(
+ username, password, request_, &io_callback_, &auth_token_[target]);
+}
+
+void HttpNetworkTransaction::AddAuthorizationHeader(
+ HttpAuth::Target target, HttpRequestHeaders* authorization_headers) {
+ DCHECK(HaveAuth(target));
+ DCHECK(!auth_token_[target].empty());
+ authorization_headers->SetHeader(
+ HttpAuth::GetAuthorizationHeaderName(target),
+ auth_token_[target]);
+ auth_token_[target].clear();
+}
+
+GURL HttpNetworkTransaction::AuthOrigin(HttpAuth::Target target) const {
+ GURL origin = PossiblyInvalidAuthOrigin(target);
+ DCHECK(origin.is_valid());
+ return origin;
+}
+
+GURL HttpNetworkTransaction::PossiblyInvalidAuthOrigin(
+ HttpAuth::Target target) const {
+ switch (target) {
+ case HttpAuth::AUTH_PROXY:
+ if (!proxy_info_.proxy_server().is_valid() ||
+ proxy_info_.proxy_server().is_direct()) {
+ return GURL(); // There is no proxy server.
+ }
+ return GURL("http://" + proxy_info_.proxy_server().host_and_port());
+ case HttpAuth::AUTH_SERVER:
+ return request_->url.GetOrigin();
+ default:
+ return GURL();
+ }
+}
+
+std::string HttpNetworkTransaction::AuthPath(HttpAuth::Target target)
+ const {
+ // Proxy authentication realms apply to all paths. So we will use
+ // empty string in place of an absolute path.
+ return target == HttpAuth::AUTH_PROXY ?
+ std::string() : request_->url.path();
+}
+
+// static
+std::string HttpNetworkTransaction::AuthTargetString(
+ HttpAuth::Target target) {
+ return target == HttpAuth::AUTH_PROXY ? "proxy" : "server";
+}
+
+void HttpNetworkTransaction::InvalidateRejectedAuthFromCache(
+ HttpAuth::Target target,
+ const GURL& auth_origin) {
+ DCHECK(HaveAuth(target));
+
+ // TODO(eroman): this short-circuit can be relaxed. If the realm of
+ // the preemptively used auth entry matches the realm of the subsequent
+ // challenge, then we can invalidate the preemptively used entry.
+ // Otherwise as-is we may send the failed credentials one extra time.
+ if (auth_identity_[target].source == HttpAuth::IDENT_SRC_PATH_LOOKUP)
+ return;
+
+ // Clear the cache entry for the identity we just failed on.
+ // Note: we require the username/password to match before invalidating
+ // since the entry in the cache may be newer than what we used last time.
+ session_->auth_cache()->Remove(auth_origin,
+ auth_handler_[target]->realm(),
+ auth_handler_[target]->scheme(),
+ auth_identity_[target].username,
+ auth_identity_[target].password);
+}
+
+bool HttpNetworkTransaction::SelectPreemptiveAuth(HttpAuth::Target target) {
+ DCHECK(!HaveAuth(target));
+
+ // Don't do preemptive authorization if the URL contains a username/password,
+ // since we must first be challenged in order to use the URL's identity.
+ if (request_->url.has_username())
+ return false;
+
+ // SelectPreemptiveAuth() is on the critical path for each request, so it
+ // is expected to be fast. LookupByPath() is fast in the common case, since
+ // the number of http auth cache entries is expected to be very small.
+ // (For most users in fact, it will be 0.)
+ HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByPath(
+ AuthOrigin(target), AuthPath(target));
+ if (!entry)
+ return false;
+
+ // Try to create a handler using the previous auth challenge.
+ scoped_ptr<HttpAuthHandler> handler_preemptive;
+ int rv_create = session_->http_auth_handler_factory()->
+ CreatePreemptiveAuthHandlerFromString(
+ entry->auth_challenge(), target, AuthOrigin(target),
+ entry->IncrementNonceCount(), net_log_, &handler_preemptive);
+ if (rv_create != OK)
+ return false;
+
+ // Set the state
+ auth_identity_[target].source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
+ auth_identity_[target].invalid = false;
+ auth_identity_[target].username = entry->username();
+ auth_identity_[target].password = entry->password();
+ auth_handler_[target].swap(handler_preemptive);
+ return true;
+}
+
+bool HttpNetworkTransaction::SelectNextAuthIdentityToTry(
+ HttpAuth::Target target,
+ const GURL& auth_origin) {
+ DCHECK(auth_handler_[target].get());
+ DCHECK(auth_identity_[target].invalid);
+
+ // Try to use the username/password encoded into the URL first.
+ if (target == HttpAuth::AUTH_SERVER && request_->url.has_username() &&
+ !embedded_identity_used_) {
+ auth_identity_[target].source = HttpAuth::IDENT_SRC_URL;
+ auth_identity_[target].invalid = false;
+ // Extract the username:password from the URL.
+ GetIdentityFromURL(request_->url,
+ &auth_identity_[target].username,
+ &auth_identity_[target].password);
+ embedded_identity_used_ = true;
+ // TODO(eroman): If the password is blank, should we also try combining
+ // with a password from the cache?
+ return true;
+ }
+
+ // Check the auth cache for a realm entry.
+ HttpAuthCache::Entry* entry =
+ session_->auth_cache()->Lookup(auth_origin, auth_handler_[target]->realm(),
+ auth_handler_[target]->scheme());
+
+ if (entry) {
+ auth_identity_[target].source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
+ auth_identity_[target].invalid = false;
+ auth_identity_[target].username = entry->username();
+ auth_identity_[target].password = entry->password();
+ return true;
+ }
+
+ // Use default credentials (single sign on) if this is the first attempt
+ // at identity. Do not allow multiple times as it will infinite loop.
+ // We use default credentials after checking the auth cache so that if
+ // single sign-on doesn't work, we won't try default credentials for future
+ // transactions.
+ if (!default_credentials_used_ &&
+ auth_handler_[target]->AllowsDefaultCredentials()) {
+ auth_identity_[target].source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
+ auth_identity_[target].invalid = false;
+ default_credentials_used_ = true;
+ return true;
+ }
+
+ return false;
+}
+
+std::string HttpNetworkTransaction::AuthChallengeLogMessage() const {
+ std::string msg;
+ std::string header_val;
+ void* iter = NULL;
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
+ while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
+ msg.append("\n Has header Proxy-Authenticate: ");
+ msg.append(header_val);
+ }
+
+ iter = NULL;
+ while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
+ msg.append("\n Has header WWW-Authenticate: ");
+ msg.append(header_val);
+ }
+
+ // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate
+ // authentication with a "Proxy-Support: Session-Based-Authentication"
+ // response header.
+ iter = NULL;
+ while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
+ msg.append("\n Has header Proxy-Support: ");
+ msg.append(header_val);
+ }
+
+ return msg;
+}
+
int HttpNetworkTransaction::HandleAuthChallenge(bool establishing_tunnel) {
scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
DCHECK(headers);
@@ -1952,41 +2177,99 @@ int HttpNetworkTransaction::HandleAuthChallenge(bool establishing_tunnel) {
return OK;
HttpAuth::Target target = status == 407 ?
HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER;
+ GURL auth_origin = PossiblyInvalidAuthOrigin(target);
+
+ LOG(INFO) << "The " << AuthTargetString(target) << " "
+ << auth_origin << " requested auth"
+ << AuthChallengeLogMessage();
+
if (target == HttpAuth::AUTH_PROXY && proxy_info_.is_direct())
return ERR_UNEXPECTED_PROXY_AUTH;
+ DCHECK(auth_origin.is_valid());
+
+ // The auth we tried just failed, hence it can't be valid. Remove it from
+ // the cache so it won't be used again.
+ // TODO(wtc): IsFinalRound is not the right condition. In a multi-round
+ // auth sequence, the server may fail the auth in round 1 if our first
+ // authorization header is broken. We should inspect response_.headers to
+ // determine if the server already failed the auth or wants us to continue.
+ // See http://crbug.com/21015.
+ if (HaveAuth(target) && auth_handler_[target]->IsFinalRound()) {
+ InvalidateRejectedAuthFromCache(target, auth_origin);
+ auth_handler_[target].reset();
+ auth_identity_[target] = HttpAuth::Identity();
+ }
+
+ auth_identity_[target].invalid = true;
+
+ if (target != HttpAuth::AUTH_SERVER ||
+ !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) {
+ // Find the best authentication challenge that we support.
+ HttpAuth::ChooseBestChallenge(session_->http_auth_handler_factory(),
+ headers, target, auth_origin, net_log_,
+ &auth_handler_[target]);
+ }
+
+ if (!auth_handler_[target].get()) {
+ if (establishing_tunnel) {
+ LOG(ERROR) << "Can't perform auth to the " << AuthTargetString(target)
+ << " " << auth_origin << " when establishing a tunnel"
+ << AuthChallengeLogMessage();
+
+ // We are establishing a tunnel, we can't show the error page because an
+ // active network attacker could control its contents. Instead, we just
+ // fail to establish the tunnel.
+ DCHECK(target == HttpAuth::AUTH_PROXY);
+ return ERR_PROXY_AUTH_REQUESTED;
+ }
+ // We found no supported challenge -- let the transaction continue
+ // so we end up displaying the error page.
+ return OK;
+ }
- int rv = auth_controllers_[target]->HandleAuthChallenge(headers,
- request_->load_flags,
- establishing_tunnel);
- if (auth_controllers_[target]->HaveAuthHandler())
- pending_auth_target_ = target;
+ if (auth_handler_[target]->NeedsIdentity()) {
+ // Pick a new auth identity to try, by looking to the URL and auth cache.
+ // If an identity to try is found, it is saved to auth_identity_[target].
+ SelectNextAuthIdentityToTry(target, auth_origin);
+ } else {
+ // Proceed with the existing identity or a null identity.
+ //
+ // TODO(wtc): Add a safeguard against infinite transaction restarts, if
+ // the server keeps returning "NTLM".
+ auth_identity_[target].invalid = false;
+ }
- scoped_refptr<AuthChallengeInfo> auth_info =
- auth_controllers_[target]->auth_info();
- if (auth_info.get())
- response_.auth_challenge = auth_info;
+ // Make a note that we are waiting for auth. This variable is inspected
+ // when the client calls RestartWithAuth() to pick up where we left off.
+ pending_auth_target_ = target;
- if (rv == ERR_AUTH_NEEDS_CANONICAL_NAME) {
- next_state_ = STATE_RESOLVE_CANONICAL_NAME;
- rv = OK;
+ if (auth_identity_[target].invalid) {
+ // We have exhausted all identity possibilities, all we can do now is
+ // pass the challenge information back to the client.
+ PopulateAuthChallenge(target, auth_origin);
}
- return rv;
+ // SPN determination (for Negotiate) requires a DNS lookup to find the
+ // canonical name. This needs to be done asynchronously to prevent blocking
+ // the IO thread.
+ if (auth_handler_[target]->NeedsCanonicalName())
+ next_state_ = STATE_RESOLVE_CANONICAL_NAME;
+
+ return OK;
}
-GURL HttpNetworkTransaction::AuthURL(HttpAuth::Target target) const {
- switch (target) {
- case HttpAuth::AUTH_PROXY:
- if (!proxy_info_.proxy_server().is_valid() ||
- proxy_info_.proxy_server().is_direct()) {
- return GURL(); // There is no proxy server.
- }
- return GURL("http://" + proxy_info_.proxy_server().host_and_port());
- case HttpAuth::AUTH_SERVER:
- return request_->url;
- default:
- return GURL();
- }
+void HttpNetworkTransaction::PopulateAuthChallenge(HttpAuth::Target target,
+ const GURL& auth_origin) {
+ // Populates response_.auth_challenge with the authentication challenge info.
+ // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
+
+ AuthChallengeInfo* auth_info = new AuthChallengeInfo;
+ auth_info->is_proxy = target == HttpAuth::AUTH_PROXY;
+ auth_info->host_and_port = ASCIIToWide(GetHostAndPort(auth_origin));
+ auth_info->scheme = ASCIIToWide(auth_handler_[target]->scheme());
+ // TODO(eroman): decode realm according to RFC 2047.
+ auth_info->realm = ASCIIToWide(auth_handler_[target]->realm());
+ response_.auth_challenge = auth_info;
}
void HttpNetworkTransaction::MarkBrokenAlternateProtocolAndFallback() {
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index f02deb4..147c5e2 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -20,7 +20,6 @@
#include "net/base/ssl_config_service.h"
#include "net/http/http_alternate_protocols.h"
#include "net/http/http_auth.h"
-#include "net/http/http_auth_controller.h"
#include "net/http/http_auth_handler.h"
#include "net/http/http_response_info.h"
#include "net/http/http_transaction.h"
@@ -241,24 +240,84 @@ class HttpNetworkTransaction : public HttpTransaction {
// Returns true if we should try to add an Authorization header.
bool ShouldApplyServerAuth() const;
+ // Adds either the proxy auth header, or the origin server auth header,
+ // as specified by |target|.
+ void AddAuthorizationHeader(
+ HttpAuth::Target target, HttpRequestHeaders* authorization_headers);
+
+ // Returns a log message for all the response headers related to the auth
+ // challenge.
+ std::string AuthChallengeLogMessage() const;
+
// Handles HTTP status code 401 or 407.
// HandleAuthChallenge() returns a network error code, or OK on success.
// May update |pending_auth_target_| or |response_.auth_challenge|.
int HandleAuthChallenge(bool establishing_tunnel);
+ // Populates response_.auth_challenge with the challenge information, so that
+ // URLRequestHttpJob can prompt for a username/password.
+ void PopulateAuthChallenge(HttpAuth::Target target,
+ const GURL& auth_origin);
+
+ // Invalidates any auth cache entries after authentication has failed.
+ // The identity that was rejected is auth_identity_[target].
+ void InvalidateRejectedAuthFromCache(HttpAuth::Target target,
+ const GURL& auth_origin);
+
+ // Sets auth_identity_[target] to the next identity that the transaction
+ // should try. It chooses candidates by searching the auth cache
+ // and the URL for a username:password. Returns true if an identity
+ // was found.
+ bool SelectNextAuthIdentityToTry(HttpAuth::Target target,
+ const GURL& auth_origin);
+
+ // Searches the auth cache for an entry that encompasses the request's path.
+ // If such an entry is found, updates auth_identity_[target] and
+ // auth_handler_[target] with the cache entry's data and returns true.
+ bool SelectPreemptiveAuth(HttpAuth::Target target);
+
bool HaveAuth(HttpAuth::Target target) const {
- return auth_controllers_[target].get() &&
- auth_controllers_[target]->HaveAuth();
+ return auth_handler_[target].get() && !auth_identity_[target].invalid;
}
- // Get the {scheme, host, path, port} for the authentication target
- GURL AuthURL(HttpAuth::Target target) const;
+ // Get the {scheme, host, port} for the authentication target
+ GURL AuthOrigin(HttpAuth::Target target) const;
+
+ // Same as AuthOrigin(), but will return an invalid GURL if the target is
+ // invalid.
+ GURL PossiblyInvalidAuthOrigin(HttpAuth::Target target) const;
+
+ // Get the absolute path of the resource needing authentication.
+ // For proxy authentication the path is always empty string.
+ std::string AuthPath(HttpAuth::Target target) const;
+
+ // Generate an authentication token for |target| if necessary. The return
+ // value is a net error code. |OK| will be returned both in the case that
+ // a token is correctly generated synchronously, as well as when no tokens
+ // were necessary.
+ int MaybeGenerateAuthToken(HttpAuth::Target target);
void MarkBrokenAlternateProtocolAndFallback();
+ // Returns a string representation of a HttpAuth::Target value that can be
+ // used in log messages.
+ static std::string AuthTargetString(HttpAuth::Target target);
+
static bool g_ignore_certificate_errors;
- scoped_ptr<HttpAuthController> auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
+ // |auth_handler_| encapsulates the logic for the particular auth-scheme.
+ // This includes the challenge's parameters. If NULL, then there is no
+ // associated auth handler.
+ scoped_ptr<HttpAuthHandler> auth_handler_[HttpAuth::AUTH_NUM_TARGETS];
+
+ // |auth_identity_| holds the (username/password) that should be used by
+ // the |auth_handler_| to generate credentials. This identity can come from
+ // a number of places (url, cache, prompt).
+ HttpAuth::Identity auth_identity_[HttpAuth::AUTH_NUM_TARGETS];
+
+ // |auth_token_| contains the opaque string to pass to the proxy or
+ // server to authenticate the client.
+ std::string auth_token_[HttpAuth::AUTH_NUM_TARGETS];
// Whether this transaction is waiting for proxy auth, server auth, or is
// not waiting for any auth at all. |pending_auth_target_| is read and
@@ -300,6 +359,15 @@ class HttpNetworkTransaction : public HttpTransaction {
// Only valid if |alternate_protocol_mode_| == kUsingAlternateProtocol.
HttpAlternateProtocols::Protocol alternate_protocol_;
+ // True if we've used the username/password embedded in the URL. This
+ // makes sure we use the embedded identity only once for the transaction,
+ // preventing an infinite auth restart loop.
+ bool embedded_identity_used_;
+
+ // True if default credentials have already been tried for this transaction
+ // in response to an HTTP authentication challenge.
+ bool default_credentials_used_;
+
SSLConfig ssl_config_;
std::string request_headers_;
diff --git a/net/net.gyp b/net/net.gyp
index 5e5d971..77f4457e 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -327,8 +327,6 @@
'http/http_auth.h',
'http/http_auth_cache.cc',
'http/http_auth_cache.h',
- 'http/http_auth_controller.cc',
- 'http/http_auth_controller.h',
'http/http_auth_filter.cc',
'http/http_auth_filter.h',
'http/http_auth_filter_win.h',