This source file includes following definitions.
- AuthChallengeLogMessage
- DetermineAuthTarget
- HistogramAuthEvent
- http_auth_handler_factory_
- MaybeGenerateAuthToken
- SelectPreemptiveAuth
- AddAuthorizationHeader
- HandleAuthChallenge
- ResetAuth
- HaveAuthHandler
- HaveAuth
- InvalidateCurrentHandler
- InvalidateRejectedAuthFromCache
- SelectNextAuthIdentityToTry
- PopulateAuthChallenge
- DisableOnAuthHandlerResult
- OnIOComplete
- auth_info
- IsAuthSchemeDisabled
- DisableAuthScheme
- DisableEmbeddedIdentity
#include "net/http/http_auth_controller.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "net/base/auth.h"
#include "net/base/net_util.h"
#include "net/dns/host_resolver.h"
#include "net/http/http_auth_handler.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"
#include "net/http/http_response_headers.h"
namespace net {
namespace {
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);
}
iter = NULL;
while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
msg.append("\n Has header Proxy-Support: ");
msg.append(header_val);
}
return msg;
}
enum AuthEvent {
AUTH_EVENT_START = 0,
AUTH_EVENT_REJECT,
AUTH_EVENT_MAX,
};
enum AuthTarget {
AUTH_TARGET_PROXY = 0,
AUTH_TARGET_SECURE_PROXY,
AUTH_TARGET_SERVER,
AUTH_TARGET_SECURE_SERVER,
AUTH_TARGET_MAX,
};
AuthTarget DetermineAuthTarget(const HttpAuthHandler* handler) {
switch (handler->target()) {
case HttpAuth::AUTH_PROXY:
if (handler->origin().SchemeIsSecure())
return AUTH_TARGET_SECURE_PROXY;
else
return AUTH_TARGET_PROXY;
case HttpAuth::AUTH_SERVER:
if (handler->origin().SchemeIsSecure())
return AUTH_TARGET_SECURE_SERVER;
else
return AUTH_TARGET_SERVER;
default:
NOTREACHED();
return AUTH_TARGET_MAX;
}
}
void HistogramAuthEvent(HttpAuthHandler* handler, AuthEvent auth_event) {
#if !defined(NDEBUG)
static base::PlatformThreadId first_thread =
base::PlatformThread::CurrentId();
DCHECK_EQ(first_thread, base::PlatformThread::CurrentId());
#endif
HttpAuth::Scheme auth_scheme = handler->auth_scheme();
DCHECK(auth_scheme >= 0 && auth_scheme < HttpAuth::AUTH_SCHEME_MAX);
static const int kEventBucketsEnd =
HttpAuth::AUTH_SCHEME_MAX * AUTH_EVENT_MAX;
int event_bucket = auth_scheme * AUTH_EVENT_MAX + auth_event;
DCHECK(event_bucket >= 0 && event_bucket < kEventBucketsEnd);
UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthCount", event_bucket,
kEventBucketsEnd);
if (auth_event != AUTH_EVENT_START)
return;
static const int kTargetBucketsEnd =
HttpAuth::AUTH_SCHEME_MAX * AUTH_TARGET_MAX;
AuthTarget auth_target = DetermineAuthTarget(handler);
int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target;
DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd);
UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket,
kTargetBucketsEnd);
}
}
HttpAuthController::HttpAuthController(
HttpAuth::Target target,
const GURL& auth_url,
HttpAuthCache* http_auth_cache,
HttpAuthHandlerFactory* http_auth_handler_factory)
: 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),
http_auth_cache_(http_auth_cache),
http_auth_handler_factory_(http_auth_handler_factory) {
}
HttpAuthController::~HttpAuthController() {
DCHECK(CalledOnValidThread());
}
int HttpAuthController::MaybeGenerateAuthToken(
const HttpRequestInfo* request, const CompletionCallback& callback,
const BoundNetLog& net_log) {
DCHECK(CalledOnValidThread());
bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log);
if (!needs_auth)
return OK;
const AuthCredentials* credentials = NULL;
if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS)
credentials = &identity_.credentials;
DCHECK(auth_token_.empty());
DCHECK(callback_.is_null());
int rv = handler_->GenerateAuthToken(
credentials, request,
base::Bind(&HttpAuthController::OnIOComplete, base::Unretained(this)),
&auth_token_);
if (DisableOnAuthHandlerResult(rv))
rv = OK;
if (rv == ERR_IO_PENDING)
callback_ = callback;
else
OnIOComplete(rv);
return rv;
}
bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) {
DCHECK(CalledOnValidThread());
DCHECK(!HaveAuth());
DCHECK(identity_.invalid);
if (auth_url_.has_username())
return false;
HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath(
auth_origin_, auth_path_);
if (!entry)
return false;
scoped_ptr<HttpAuthHandler> handler_preemptive;
int rv_create = http_auth_handler_factory_->
CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_,
auth_origin_,
entry->IncrementNonceCount(),
net_log, &handler_preemptive);
if (rv_create != OK)
return false;
identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
identity_.invalid = false;
identity_.credentials = entry->credentials();
handler_.swap(handler_preemptive);
return true;
}
void HttpAuthController::AddAuthorizationHeader(
HttpRequestHeaders* authorization_headers) {
DCHECK(CalledOnValidThread());
DCHECK(HaveAuth());
if (!auth_token_.empty()) {
authorization_headers->SetHeader(
HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
auth_token_.clear();
}
}
int HttpAuthController::HandleAuthChallenge(
scoped_refptr<HttpResponseHeaders> headers,
bool do_not_send_server_auth,
bool establishing_tunnel,
const BoundNetLog& net_log) {
DCHECK(CalledOnValidThread());
DCHECK(headers.get());
DCHECK(auth_origin_.is_valid());
VLOG(1) << "The " << HttpAuth::GetAuthTargetString(target_) << " "
<< auth_origin_ << " requested auth "
<< AuthChallengeLogMessage(headers.get());
if (HaveAuth()) {
std::string challenge_used;
HttpAuth::AuthorizationResult result =
HttpAuth::HandleChallengeResponse(handler_.get(),
headers.get(),
target_,
disabled_schemes_,
&challenge_used);
switch (result) {
case HttpAuth::AUTHORIZATION_RESULT_ACCEPT:
break;
case HttpAuth::AUTHORIZATION_RESULT_INVALID:
InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
break;
case HttpAuth::AUTHORIZATION_RESULT_REJECT:
HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
break;
case HttpAuth::AUTHORIZATION_RESULT_STALE:
if (http_auth_cache_->UpdateStaleChallenge(auth_origin_,
handler_->realm(),
handler_->auth_scheme(),
challenge_used)) {
InvalidateCurrentHandler(INVALIDATE_HANDLER);
} else {
InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
}
break;
case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
InvalidateCurrentHandler(
(identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ?
INVALIDATE_HANDLER :
INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
break;
default:
NOTREACHED();
break;
}
}
identity_.invalid = true;
bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER ||
!do_not_send_server_auth);
do {
if (!handler_.get() && can_send_auth) {
HttpAuth::ChooseBestChallenge(http_auth_handler_factory_,
headers.get(),
target_,
auth_origin_,
disabled_schemes_,
net_log,
&handler_);
if (handler_.get())
HistogramAuthEvent(handler_.get(), AUTH_EVENT_START);
}
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());
DCHECK(target_ == HttpAuth::AUTH_PROXY);
return ERR_PROXY_AUTH_UNSUPPORTED;
}
return OK;
}
if (handler_->NeedsIdentity()) {
SelectNextAuthIdentityToTry();
} else {
identity_.invalid = false;
}
if (identity_.invalid) {
if (!handler_->AllowsExplicitCredentials()) {
HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
} else {
PopulateAuthChallenge();
}
} else {
auth_info_ = NULL;
}
} while(!handler_.get());
return OK;
}
void HttpAuthController::ResetAuth(const AuthCredentials& credentials) {
DCHECK(CalledOnValidThread());
DCHECK(identity_.invalid || credentials.Empty());
if (identity_.invalid) {
identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
identity_.invalid = false;
identity_.credentials = credentials;
}
DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
switch (identity_.source) {
case HttpAuth::IDENT_SRC_NONE:
case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
break;
default:
http_auth_cache_->Add(auth_origin_, handler_->realm(),
handler_->auth_scheme(), handler_->challenge(),
identity_.credentials, auth_path_);
break;
}
}
bool HttpAuthController::HaveAuthHandler() const {
return handler_.get() != NULL;
}
bool HttpAuthController::HaveAuth() const {
return handler_.get() && !identity_.invalid;
}
void HttpAuthController::InvalidateCurrentHandler(
InvalidateHandlerAction action) {
DCHECK(CalledOnValidThread());
DCHECK(handler_.get());
if (action == INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS)
InvalidateRejectedAuthFromCache();
if (action == INVALIDATE_HANDLER_AND_DISABLE_SCHEME)
DisableAuthScheme(handler_->auth_scheme());
handler_.reset();
identity_ = HttpAuth::Identity();
}
void HttpAuthController::InvalidateRejectedAuthFromCache() {
DCHECK(CalledOnValidThread());
DCHECK(HaveAuth());
http_auth_cache_->Remove(auth_origin_, handler_->realm(),
handler_->auth_scheme(), identity_.credentials);
}
bool HttpAuthController::SelectNextAuthIdentityToTry() {
DCHECK(CalledOnValidThread());
DCHECK(handler_.get());
DCHECK(identity_.invalid);
if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
!embedded_identity_used_) {
identity_.source = HttpAuth::IDENT_SRC_URL;
identity_.invalid = false;
base::string16 username;
base::string16 password;
GetIdentityFromURL(auth_url_, &username, &password);
identity_.credentials.Set(username, password);
embedded_identity_used_ = true;
UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true);
return true;
}
HttpAuthCache::Entry* entry =
http_auth_cache_->Lookup(auth_origin_, handler_->realm(),
handler_->auth_scheme());
if (entry) {
identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
identity_.invalid = false;
identity_.credentials = entry->credentials();
return true;
}
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() {
DCHECK(CalledOnValidThread());
auth_info_ = new AuthChallengeInfo;
auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY);
auth_info_->challenger = HostPortPair::FromURL(auth_origin_);
auth_info_->scheme = HttpAuth::SchemeToString(handler_->auth_scheme());
auth_info_->realm = handler_->realm();
}
bool HttpAuthController::DisableOnAuthHandlerResult(int result) {
DCHECK(CalledOnValidThread());
switch (result) {
case ERR_MISSING_AUTH_CREDENTIALS:
case ERR_UNSUPPORTED_AUTH_SCHEME:
case ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS:
case ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS:
case ERR_MISCONFIGURED_AUTH_ENVIRONMENT:
DisableAuthScheme(handler_->auth_scheme());
auth_token_.clear();
return true;
default:
return false;
}
}
void HttpAuthController::OnIOComplete(int result) {
DCHECK(CalledOnValidThread());
if (DisableOnAuthHandlerResult(result))
result = OK;
if (!callback_.is_null()) {
CompletionCallback c = callback_;
callback_.Reset();
c.Run(result);
}
}
scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() {
DCHECK(CalledOnValidThread());
return auth_info_;
}
bool HttpAuthController::IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const {
DCHECK(CalledOnValidThread());
return disabled_schemes_.find(scheme) != disabled_schemes_.end();
}
void HttpAuthController::DisableAuthScheme(HttpAuth::Scheme scheme) {
DCHECK(CalledOnValidThread());
disabled_schemes_.insert(scheme);
}
void HttpAuthController::DisableEmbeddedIdentity() {
DCHECK(CalledOnValidThread());
embedded_identity_used_ = true;
}
}