// Copyright (c) 2011 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.h" #include #include "base/basictypes.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "net/base/net_errors.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" namespace net { HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {} // static void HttpAuth::ChooseBestChallenge( HttpAuthHandlerFactory* http_auth_handler_factory, const HttpResponseHeaders* headers, Target target, const GURL& origin, const std::set& disabled_schemes, const BoundNetLog& net_log, scoped_ptr* handler) { DCHECK(http_auth_handler_factory); DCHECK(handler->get() == NULL); // Choose the challenge whose authentication handler gives the maximum score. scoped_ptr best; const std::string header_name = GetChallengeHeaderName(target); std::string cur_challenge; void* iter = NULL; while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) { scoped_ptr cur; int rv = http_auth_handler_factory->CreateAuthHandlerFromString( cur_challenge, target, origin, net_log, &cur); if (rv != OK) { VLOG(1) << "Unable to create AuthHandler. Status: " << ErrorToString(rv) << " Challenge: " << cur_challenge; continue; } if (cur.get() && (!best.get() || best->score() < cur->score()) && (disabled_schemes.find(cur->auth_scheme()) == disabled_schemes.end())) best.swap(cur); } handler->swap(best); } // static HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( HttpAuthHandler* handler, const HttpResponseHeaders* headers, Target target, const std::set& disabled_schemes, std::string* challenge_used) { DCHECK(handler); DCHECK(headers); DCHECK(challenge_used); challenge_used->clear(); HttpAuth::Scheme current_scheme = handler->auth_scheme(); if (disabled_schemes.find(current_scheme) != disabled_schemes.end()) return HttpAuth::AUTHORIZATION_RESULT_REJECT; std::string current_scheme_name = SchemeToString(current_scheme); const std::string header_name = GetChallengeHeaderName(target); void* iter = NULL; std::string challenge; HttpAuth::AuthorizationResult authorization_result = HttpAuth::AUTHORIZATION_RESULT_INVALID; while (headers->EnumerateHeader(&iter, header_name, &challenge)) { HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); if (!LowerCaseEqualsASCII(props.scheme(), current_scheme_name.c_str())) continue; authorization_result = handler->HandleAnotherChallenge(&props); if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) { *challenge_used = challenge; return authorization_result; } } // Finding no matches is equivalent to rejection. return HttpAuth::AUTHORIZATION_RESULT_REJECT; } HttpAuth::ChallengeTokenizer::ChallengeTokenizer( std::string::const_iterator begin, std::string::const_iterator end) : begin_(begin), end_(end), scheme_begin_(begin), scheme_end_(begin), params_begin_(end), params_end_(end) { Init(begin, end); } HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs() const { return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ','); } std::string HttpAuth::ChallengeTokenizer::base64_param() const { // Strip off any padding. // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.) // // Our base64 decoder requires that the length be a multiple of 4. int encoded_length = params_end_ - params_begin_; while (encoded_length > 0 && encoded_length % 4 != 0 && params_begin_[encoded_length - 1] == '=') { --encoded_length; } return std::string(params_begin_, params_begin_ + encoded_length); } void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, std::string::const_iterator end) { // The first space-separated token is the auth-scheme. // NOTE: we are more permissive than RFC 2617 which says auth-scheme // is separated by 1*SP. base::StringTokenizer tok(begin, end, HTTP_LWS); if (!tok.GetNext()) { // Default param and scheme iterators provide empty strings return; } // Save the scheme's position. scheme_begin_ = tok.token_begin(); scheme_end_ = tok.token_end(); params_begin_ = scheme_end_; params_end_ = end; HttpUtil::TrimLWS(¶ms_begin_, ¶ms_end_); } // static std::string HttpAuth::GetChallengeHeaderName(Target target) { switch (target) { case AUTH_PROXY: return "Proxy-Authenticate"; case AUTH_SERVER: return "WWW-Authenticate"; default: NOTREACHED(); return std::string(); } } // static std::string HttpAuth::GetAuthorizationHeaderName(Target target) { switch (target) { case AUTH_PROXY: return HttpRequestHeaders::kProxyAuthorization; case AUTH_SERVER: return HttpRequestHeaders::kAuthorization; default: NOTREACHED(); return std::string(); } } // static std::string HttpAuth::GetAuthTargetString(Target target) { switch (target) { case AUTH_PROXY: return "proxy"; case AUTH_SERVER: return "server"; default: NOTREACHED(); return std::string(); } } // static const char* HttpAuth::SchemeToString(Scheme scheme) { static const char* const kSchemeNames[] = { "basic", "digest", "ntlm", "negotiate", "spdyproxy", "mock", }; COMPILE_ASSERT(arraysize(kSchemeNames) == AUTH_SCHEME_MAX, http_auth_scheme_names_incorrect_size); if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) { NOTREACHED(); return "invalid_scheme"; } return kSchemeNames[scheme]; } } // namespace net